diff --git a/bugsnag-android-core/detekt-baseline.xml b/bugsnag-android-core/detekt-baseline.xml
index b2c62871dd..d867422231 100644
--- a/bugsnag-android-core/detekt-baseline.xml
+++ b/bugsnag-android-core/detekt-baseline.xml
@@ -20,7 +20,9 @@
MagicNumber:DefaultDelivery.kt$DefaultDelivery$429
MagicNumber:DefaultDelivery.kt$DefaultDelivery$499
MagicNumber:LastRunInfoStore.kt$LastRunInfoStore$3
+ MaxLineLength:EventSerializationTest.kt$EventSerializationTest.Companion$it.threads.add(Thread(5, "main", ThreadType.ANDROID, true, Thread.State.RUNNABLE, stacktrace, NoopLogger))
MaxLineLength:LastRunInfo.kt$LastRunInfo$return "LastRunInfo(consecutiveLaunchCrashes=$consecutiveLaunchCrashes, crashed=$crashed, crashedDuringLaunch=$crashedDuringLaunch)"
+ MaxLineLength:ThreadState.kt$ThreadState$Thread(thread.id, thread.name, ThreadType.ANDROID, errorThread, Thread.State.forThread(thread), stacktrace, logger)
ProtectedMemberInFinalClass:ConfigInternal.kt$ConfigInternal$protected val plugins = HashSet<Plugin>()
ProtectedMemberInFinalClass:EventInternal.kt$EventInternal$protected fun isAnr(event: Event): Boolean
ProtectedMemberInFinalClass:EventInternal.kt$EventInternal$protected fun shouldDiscardClass(): Boolean
diff --git a/bugsnag-android-core/src/main/java/com/bugsnag/android/Thread.java b/bugsnag-android-core/src/main/java/com/bugsnag/android/Thread.java
index 90daa50e49..c0e469b2ae 100644
--- a/bugsnag-android-core/src/main/java/com/bugsnag/android/Thread.java
+++ b/bugsnag-android-core/src/main/java/com/bugsnag/android/Thread.java
@@ -1,6 +1,7 @@
package com.bugsnag.android;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import java.io.IOException;
import java.util.List;
@@ -19,9 +20,10 @@ public class Thread implements JsonStream.Streamable {
@NonNull String name,
@NonNull ThreadType type,
boolean errorReportingThread,
+ @NonNull Thread.State state,
@NonNull Stacktrace stacktrace,
@NonNull Logger logger) {
- this.impl = new ThreadInternal(id, name, type, errorReportingThread, stacktrace);
+ this.impl = new ThreadInternal(id, name, type, errorReportingThread, state, stacktrace);
this.logger = logger;
}
@@ -81,6 +83,25 @@ public ThreadType getType() {
return impl.getType();
}
+ /**
+ * Sets the state of thread (from {@link java.lang.Thread})
+ */
+ public void setState(@NonNull Thread.State threadState) {
+ if (threadState != null) {
+ impl.setState(threadState);
+ } else {
+ logNull("state");
+ }
+ }
+
+ /**
+ * Gets the state of the thread (from {@link java.lang.Thread})
+ */
+ @NonNull
+ public Thread.State getState() {
+ return impl.getState();
+ }
+
/**
* Gets whether the thread was the thread that caused the event
*/
@@ -111,4 +132,66 @@ public List getStacktrace() {
public void toStream(@NonNull JsonStream stream) throws IOException {
impl.toStream(stream);
}
+
+ /**
+ * The state of a reported {@link Thread}. These states correspond directly to
+ * {@link java.lang.Thread.State}, except for {@code UNKNOWN} which indicates that
+ * a state could not be captured or mapped.
+ */
+ public enum State {
+ NEW,
+ BLOCKED,
+ RUNNABLE,
+ TERMINATED,
+ TIMED_WAITING,
+ WAITING,
+ UNKNOWN;
+
+ @NonNull
+ public static State forThread(@NonNull java.lang.Thread thread) {
+ java.lang.Thread.State state = thread.getState();
+ return getState(state);
+ }
+
+ /**
+ * An exception-safe wrapper for {@link #valueOf(String)} which also handles {@code null}
+ * names. This method is used in-preference to the standard {@code valueOf} as it will
+ * return {@link #UNKNOWN} instead of throwing an exception.
+ *
+ * @param name the name of the state constant to lookup
+ * @return the named {@link State} or {@link #UNKNOWN}
+ */
+ @NonNull
+ public static State byName(@Nullable String name) {
+ if (name == null) {
+ return UNKNOWN;
+ }
+
+ try {
+ return valueOf(name);
+ } catch (IllegalArgumentException iae) {
+ return UNKNOWN;
+ }
+ }
+
+ @NonNull
+ private static State getState(java.lang.Thread.State state) {
+ switch (state) {
+ case NEW:
+ return NEW;
+ case BLOCKED:
+ return BLOCKED;
+ case RUNNABLE:
+ return RUNNABLE;
+ case TERMINATED:
+ return TERMINATED;
+ case TIMED_WAITING:
+ return TIMED_WAITING;
+ case WAITING:
+ return WAITING;
+ default:
+ return UNKNOWN;
+ }
+ }
+ }
}
diff --git a/bugsnag-android-core/src/main/java/com/bugsnag/android/ThreadInternal.kt b/bugsnag-android-core/src/main/java/com/bugsnag/android/ThreadInternal.kt
index 459c17f87e..0882742fe8 100644
--- a/bugsnag-android-core/src/main/java/com/bugsnag/android/ThreadInternal.kt
+++ b/bugsnag-android-core/src/main/java/com/bugsnag/android/ThreadInternal.kt
@@ -7,6 +7,7 @@ class ThreadInternal internal constructor(
var name: String,
var type: ThreadType,
val isErrorReportingThread: Boolean,
+ var state: Thread.State,
stacktrace: Stacktrace
) : JsonStream.Streamable {
@@ -18,6 +19,7 @@ class ThreadInternal internal constructor(
writer.name("id").value(id)
writer.name("name").value(name)
writer.name("type").value(type.desc)
+ writer.name("state").value(state.name)
writer.name("stacktrace")
writer.beginArray()
diff --git a/bugsnag-android-core/src/main/java/com/bugsnag/android/ThreadState.kt b/bugsnag-android-core/src/main/java/com/bugsnag/android/ThreadState.kt
index ea27eb92a8..06e00268ac 100644
--- a/bugsnag-android-core/src/main/java/com/bugsnag/android/ThreadState.kt
+++ b/bugsnag-android-core/src/main/java/com/bugsnag/android/ThreadState.kt
@@ -67,7 +67,7 @@ internal class ThreadState @Suppress("LongParameterList") @JvmOverloads construc
if (trace != null) {
val stacktrace = Stacktrace(trace, projectPackages, logger)
val errorThread = thread.id == currentThreadId
- Thread(thread.id, thread.name, ThreadType.ANDROID, errorThread, stacktrace, logger)
+ Thread(thread.id, thread.name, ThreadType.ANDROID, errorThread, Thread.State.forThread(thread), stacktrace, logger)
} else {
null
}
diff --git a/bugsnag-android-core/src/test/java/com/bugsnag/android/EventSerializationTest.kt b/bugsnag-android-core/src/test/java/com/bugsnag/android/EventSerializationTest.kt
index a06f233667..fcc4e48c72 100644
--- a/bugsnag-android-core/src/test/java/com/bugsnag/android/EventSerializationTest.kt
+++ b/bugsnag-android-core/src/test/java/com/bugsnag/android/EventSerializationTest.kt
@@ -40,7 +40,7 @@ internal class EventSerializationTest {
createEvent {
val stacktrace = Stacktrace(arrayOf(), emptySet(), NoopLogger)
it.threads.clear()
- it.threads.add(Thread(5, "main", ThreadType.ANDROID, true, stacktrace, NoopLogger))
+ it.threads.add(Thread(5, "main", ThreadType.ANDROID, true, Thread.State.RUNNABLE, stacktrace, NoopLogger))
},
// threads included
diff --git a/bugsnag-android-core/src/test/java/com/bugsnag/android/ThreadFacadeTest.java b/bugsnag-android-core/src/test/java/com/bugsnag/android/ThreadFacadeTest.java
index 1f4c3b5cb6..e0c747b409 100644
--- a/bugsnag-android-core/src/test/java/com/bugsnag/android/ThreadFacadeTest.java
+++ b/bugsnag-android-core/src/test/java/com/bugsnag/android/ThreadFacadeTest.java
@@ -27,7 +27,15 @@ public void setUp() {
logger = new InterceptingLogger();
List frames = Collections.emptyList();
stacktrace = new Stacktrace(frames);
- thread = new Thread(1, "thread-2", ThreadType.ANDROID, false, stacktrace, logger);
+ thread = new Thread(
+ 1,
+ "thread-2",
+ ThreadType.ANDROID,
+ false,
+ Thread.State.RUNNABLE,
+ stacktrace,
+ logger
+ );
}
@Test
diff --git a/bugsnag-android-core/src/test/java/com/bugsnag/android/ThreadSerializationTest.kt b/bugsnag-android-core/src/test/java/com/bugsnag/android/ThreadSerializationTest.kt
index c60b0bf50d..41e2cc0506 100644
--- a/bugsnag-android-core/src/test/java/com/bugsnag/android/ThreadSerializationTest.kt
+++ b/bugsnag-android-core/src/test/java/com/bugsnag/android/ThreadSerializationTest.kt
@@ -24,6 +24,7 @@ internal class ThreadSerializationTest {
"main-one",
ThreadType.ANDROID,
true,
+ Thread.State.RUNNABLE,
Stacktrace(
stacktrace,
emptySet(),
@@ -43,6 +44,7 @@ internal class ThreadSerializationTest {
"main-one",
ThreadType.ANDROID,
false,
+ Thread.State.RUNNABLE,
Stacktrace(
stacktrace1,
emptySet(),
@@ -76,6 +78,7 @@ internal class ThreadSerializationTest {
"main-one",
ThreadType.ANDROID,
true,
+ Thread.State.RUNNABLE,
trace,
NoopLogger
)
@@ -98,6 +101,7 @@ internal class ThreadSerializationTest {
"main-one",
ThreadType.ANDROID,
false,
+ Thread.State.RUNNABLE,
trace,
NoopLogger
)
diff --git a/bugsnag-android-core/src/test/resources/event_serialization_5.json b/bugsnag-android-core/src/test/resources/event_serialization_5.json
index 3b016294e0..b4e2dc7a76 100644
--- a/bugsnag-android-core/src/test/resources/event_serialization_5.json
+++ b/bugsnag-android-core/src/test/resources/event_serialization_5.json
@@ -37,6 +37,7 @@
"id": 5,
"name": "main",
"type": "android",
+ "state": "RUNNABLE",
"stacktrace": [],
"errorReportingThread": true
}
diff --git a/bugsnag-android-core/src/test/resources/thread_serialization_0.json b/bugsnag-android-core/src/test/resources/thread_serialization_0.json
index ee01654e3e..d6a34a802d 100644
--- a/bugsnag-android-core/src/test/resources/thread_serialization_0.json
+++ b/bugsnag-android-core/src/test/resources/thread_serialization_0.json
@@ -2,6 +2,7 @@
"id": 24,
"name": "main-one",
"type": "android",
+ "state": "RUNNABLE",
"stacktrace": [
{
"method": "run_func",
diff --git a/bugsnag-android-core/src/test/resources/thread_serialization_1.json b/bugsnag-android-core/src/test/resources/thread_serialization_1.json
index 654bd10fe4..ec5ffc71b9 100644
--- a/bugsnag-android-core/src/test/resources/thread_serialization_1.json
+++ b/bugsnag-android-core/src/test/resources/thread_serialization_1.json
@@ -2,6 +2,7 @@
"id": 24,
"name": "main-one",
"type": "android",
+ "state": "RUNNABLE",
"stacktrace": [
{
"method": "run_func",
diff --git a/bugsnag-android-core/src/test/resources/thread_serialization_2.json b/bugsnag-android-core/src/test/resources/thread_serialization_2.json
index ee01654e3e..d6a34a802d 100644
--- a/bugsnag-android-core/src/test/resources/thread_serialization_2.json
+++ b/bugsnag-android-core/src/test/resources/thread_serialization_2.json
@@ -2,6 +2,7 @@
"id": 24,
"name": "main-one",
"type": "android",
+ "state": "RUNNABLE",
"stacktrace": [
{
"method": "run_func",
diff --git a/bugsnag-android-core/src/test/resources/thread_serialization_3.json b/bugsnag-android-core/src/test/resources/thread_serialization_3.json
index 654bd10fe4..ec5ffc71b9 100644
--- a/bugsnag-android-core/src/test/resources/thread_serialization_3.json
+++ b/bugsnag-android-core/src/test/resources/thread_serialization_3.json
@@ -2,6 +2,7 @@
"id": 24,
"name": "main-one",
"type": "android",
+ "state": "RUNNABLE",
"stacktrace": [
{
"method": "run_func",
diff --git a/bugsnag-plugin-react-native/src/main/java/com/bugsnag/android/ThreadDeserializer.java b/bugsnag-plugin-react-native/src/main/java/com/bugsnag/android/ThreadDeserializer.java
index 4cddfc3672..fd7dae52ac 100644
--- a/bugsnag-plugin-react-native/src/main/java/com/bugsnag/android/ThreadDeserializer.java
+++ b/bugsnag-plugin-react-native/src/main/java/com/bugsnag/android/ThreadDeserializer.java
@@ -32,6 +32,7 @@ public Thread deserialize(Map map) {
MapUtils.getOrThrow(map, "name"),
ThreadType.valueOf(type.toUpperCase(Locale.US)),
errorReportingThread,
+ Thread.State.byName(MapUtils.getOrThrow(map, "state")),
new Stacktrace(frames),
logger
);
diff --git a/bugsnag-plugin-react-native/src/test/java/com/bugsnag/android/EventDeserializerTest.kt b/bugsnag-plugin-react-native/src/test/java/com/bugsnag/android/EventDeserializerTest.kt
index 0f4141c3a2..767b6f33f4 100644
--- a/bugsnag-plugin-react-native/src/test/java/com/bugsnag/android/EventDeserializerTest.kt
+++ b/bugsnag-plugin-react-native/src/test/java/com/bugsnag/android/EventDeserializerTest.kt
@@ -56,6 +56,7 @@ class EventDeserializerTest {
"id" to 52L,
"type" to "reactnativejs",
"name" to "thread-worker-02",
+ "state" to "RUNNABLE",
"errorReportingThread" to true
)
diff --git a/bugsnag-plugin-react-native/src/test/java/com/bugsnag/android/ThreadDeserializerTest.kt b/bugsnag-plugin-react-native/src/test/java/com/bugsnag/android/ThreadDeserializerTest.kt
index 71e4ac1db0..f2018e03b4 100644
--- a/bugsnag-plugin-react-native/src/test/java/com/bugsnag/android/ThreadDeserializerTest.kt
+++ b/bugsnag-plugin-react-native/src/test/java/com/bugsnag/android/ThreadDeserializerTest.kt
@@ -25,6 +25,7 @@ class ThreadDeserializerTest {
map["id"] = 52L
map["type"] = "reactnativejs"
map["name"] = "thread-worker-02"
+ map["state"] = "RUNNABLE"
map["errorReportingThread"] = true
}
diff --git a/bugsnag-plugin-react-native/src/test/java/com/bugsnag/android/ThreadSerializerTest.java b/bugsnag-plugin-react-native/src/test/java/com/bugsnag/android/ThreadSerializerTest.java
index 319cfadd22..2060ba0ecc 100644
--- a/bugsnag-plugin-react-native/src/test/java/com/bugsnag/android/ThreadSerializerTest.java
+++ b/bugsnag-plugin-react-native/src/test/java/com/bugsnag/android/ThreadSerializerTest.java
@@ -37,7 +37,7 @@ public void setup() {
List frames = Collections.singletonList(stackframe);
Stacktrace stacktrace = new Stacktrace(frames);
thread = new Thread(1, "fake-thread", ThreadType.ANDROID,
- true, stacktrace, NoopLogger.INSTANCE);
+ true, Thread.State.RUNNABLE, stacktrace, NoopLogger.INSTANCE);
}
@Test