From c73f990a7cfe8d847376cc49609102cc35c2a8f8 Mon Sep 17 00:00:00 2001 From: Jason Date: Wed, 15 Sep 2021 08:09:30 +0100 Subject: [PATCH] fix(thread state): added nullability annotation to `Thread.State.getDescriptor` --- CHANGELOG.md | 5 ++ .../main/java/com/bugsnag/android/Thread.java | 49 ++++++++++++------- .../com/bugsnag/android/ThreadInternal.kt | 2 +- .../bugsnag/android/ThreadDeserializer.java | 2 +- .../com/bugsnag/android/ThreadSerializer.kt | 1 + 5 files changed, 39 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 71c649cc8c..c09fc9bf38 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## TBD + +* Capture and report Thread.state for Android Runtime threads + [#1367](https://github.com/bugsnag/bugsnag-android/pull/1367) + ## 5.13.0 (2021-09-22) * Capture breadcrumbs for OkHttp network requests 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 c0e469b2ae..d6d5ee73fe 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 @@ -139,13 +139,24 @@ public void toStream(@NonNull JsonStream stream) throws IOException { * a state could not be captured or mapped. */ public enum State { - NEW, - BLOCKED, - RUNNABLE, - TERMINATED, - TIMED_WAITING, - WAITING, - UNKNOWN; + NEW("NEW"), + BLOCKED("BLOCKED"), + RUNNABLE("RUNNABLE"), + TERMINATED("TERMINATED"), + TIMED_WAITING("TIMED_WAITING"), + WAITING("WAITING"), + UNKNOWN("UNKNOWN"); + + private final String descriptor; + + State(String descriptor) { + this.descriptor = descriptor; + } + + @NonNull + public String getDescriptor() { + return descriptor; + } @NonNull public static State forThread(@NonNull java.lang.Thread thread) { @@ -154,24 +165,26 @@ public static State forThread(@NonNull java.lang.Thread thread) { } /** - * 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. + * Lookup the {@code State} for a given {@link #getDescriptor() descriptor} code. Unlike + * {@link #valueOf(String) valueOf}, this method will return {@link #UNKNOWN} is no + * matching {@code State} constant can be found. * - * @param name the name of the state constant to lookup - * @return the named {@link State} or {@link #UNKNOWN} + * @param descriptor a consistent descriptor of the state constant to lookup + * @return the requested {@link State} or {@link #UNKNOWN} */ @NonNull - public static State byName(@Nullable String name) { - if (name == null) { + public static State byDescriptor(@Nullable String descriptor) { + if (descriptor == null) { return UNKNOWN; } - try { - return valueOf(name); - } catch (IllegalArgumentException iae) { - return UNKNOWN; + for (State state : values()) { + if (state.getDescriptor().equals(descriptor)) { + return state; + } } + + return UNKNOWN; } @NonNull 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 0882742fe8..efacfb5644 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 @@ -19,7 +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("state").value(state.descriptor) writer.name("stacktrace") writer.beginArray() 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 fd7dae52ac..6259e5f62c 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,7 +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")), + Thread.State.byDescriptor(MapUtils.getOrThrow(map, "state")), new Stacktrace(frames), logger ); diff --git a/bugsnag-plugin-react-native/src/main/java/com/bugsnag/android/ThreadSerializer.kt b/bugsnag-plugin-react-native/src/main/java/com/bugsnag/android/ThreadSerializer.kt index 25e3c1fd6f..fd046df89c 100644 --- a/bugsnag-plugin-react-native/src/main/java/com/bugsnag/android/ThreadSerializer.kt +++ b/bugsnag-plugin-react-native/src/main/java/com/bugsnag/android/ThreadSerializer.kt @@ -8,6 +8,7 @@ internal class ThreadSerializer : MapSerializer { map["name"] = thread.name map["type"] = thread.type.toString().toLowerCase(Locale.US) map["errorReportingThread"] = thread.errorReportingThread + map["state"] = thread.state.descriptor map["stacktrace"] = thread.stacktrace.map { val frame = mutableMapOf()