diff --git a/CHANGELOG.md b/CHANGELOG.md index f14da5290f..6bcf27d477 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ * Update project to build using Gradle/AGP 7 [#1354](https://github.com/bugsnag/bugsnag-android/pull/1354) +* Capture and report Thread.state for Android Runtime threads + []() + ## 5.12.0 (2021-08-26) * The `app.lowMemory` value always report the most recent `onTrimMemory`/`onLowMemory` status diff --git a/bugsnag-android-core/detekt-baseline.xml b/bugsnag-android-core/detekt-baseline.xml index 3dff843d88..d867422231 100644 --- a/bugsnag-android-core/detekt-baseline.xml +++ b/bugsnag-android-core/detekt-baseline.xml @@ -1,6 +1,6 @@ - + - + ImplicitDefaultLocale:DeliveryHeaders.kt$String.format("%02x", byte) LongParameterList:App.kt$App$( /** * The architecture of the running application binary */ var binaryArch: String?, /** * The package name of the application */ var id: String?, /** * The release stage set in [Configuration.releaseStage] */ var releaseStage: String?, /** * The version of the application set in [Configuration.version] */ var version: String?, /** The revision ID from the manifest (React Native apps only) */ var codeBundleId: String?, /** * The unique identifier for the build of the application set in [Configuration.buildUuid] */ var buildUuid: String?, /** * The application type set in [Configuration#version] */ var type: String?, /** * The version code of the application set in [Configuration.versionCode] */ var versionCode: Number? ) @@ -8,25 +8,27 @@ LongParameterList:AppWithState.kt$AppWithState$( binaryArch: String?, id: String?, releaseStage: String?, version: String?, codeBundleId: String?, buildUuid: String?, type: String?, versionCode: Number?, /** * The number of milliseconds the application was running before the event occurred */ var duration: Number?, /** * The number of milliseconds the application was running in the foreground before the * event occurred */ var durationInForeground: Number?, /** * Whether the application was in the foreground when the event occurred */ var inForeground: Boolean?, /** * Whether the application was launching when the event occurred */ var isLaunching: Boolean? ) LongParameterList:AppWithState.kt$AppWithState$( config: ImmutableConfig, binaryArch: String?, id: String?, releaseStage: String?, version: String?, codeBundleId: String?, duration: Number?, durationInForeground: Number?, inForeground: Boolean?, isLaunching: Boolean? ) LongParameterList:DataCollectionModule.kt$DataCollectionModule$( contextModule: ContextModule, configModule: ConfigModule, systemServiceModule: SystemServiceModule, trackerModule: TrackerModule, bgTaskService: BackgroundTaskService, connectivity: Connectivity, deviceId: String?, memoryTrimState: MemoryTrimState ) - LongParameterList:Device.kt$Device$( buildInfo: DeviceBuildInfo, /** * The Application Binary Interface used */ var cpuAbi: Array<String>?, /** * Whether the device has been jailbroken */ var jailbroken: Boolean?, /** * A UUID generated by Bugsnag and used for the individual application on a device */ var id: String?, /** * The IETF language tag of the locale used */ var locale: String?, /** * The total number of bytes of memory on the device */ var totalMemory: Long?, /** * A collection of names and their versions of the primary languages, frameworks or * runtimes that the application is running on */ var runtimeVersions: MutableMap<String, Any>? ) - LongParameterList:DeviceBuildInfo.kt$DeviceBuildInfo$( val manufacturer: String?, val model: String?, val osVersion: String?, val apiLevel: Int?, val osBuild: String?, val fingerprint: String?, val tags: String?, val brand: String?, val cpuAbis: Array<String>? ) + LongParameterList:Device.kt$Device$( buildInfo: DeviceBuildInfo, /** * The Application Binary Interface used */ var cpuAbi: Array<String>?, /** * Whether the device has been jailbroken */ var jailbroken: Boolean?, /** * A UUID generated by Bugsnag and used for the individual application on a device */ var id: String?, /** * The IETF language tag of the locale used */ var locale: String?, /** * The total number of bytes of memory on the device */ var totalMemory: Long?, /** * A collection of names and their versions of the primary languages, frameworks or * runtimes that the application is running on */ var runtimeVersions: MutableMap<String, Any>? ) + LongParameterList:DeviceBuildInfo.kt$DeviceBuildInfo$( val manufacturer: String?, val model: String?, val osVersion: String?, val apiLevel: Int?, val osBuild: String?, val fingerprint: String?, val tags: String?, val brand: String?, val cpuAbis: Array<String>? ) LongParameterList:DeviceDataCollector.kt$DeviceDataCollector$( private val connectivity: Connectivity, private val appContext: Context, resources: Resources, private val deviceId: String?, private val buildInfo: DeviceBuildInfo, private val dataDirectory: File, rootDetector: RootDetector, private val bgTaskService: BackgroundTaskService, private val logger: Logger ) - LongParameterList:DeviceWithState.kt$DeviceWithState$( buildInfo: DeviceBuildInfo, jailbroken: Boolean?, id: String?, locale: String?, totalMemory: Long?, runtimeVersions: MutableMap<String, Any>, /** * The number of free bytes of storage available on the device */ var freeDisk: Long?, /** * The number of free bytes of memory available on the device */ var freeMemory: Long?, /** * The orientation of the device when the event occurred: either portrait or landscape */ var orientation: String?, /** * The timestamp on the device when the event occurred */ var time: Date? ) + LongParameterList:DeviceWithState.kt$DeviceWithState$( buildInfo: DeviceBuildInfo, jailbroken: Boolean?, id: String?, locale: String?, totalMemory: Long?, runtimeVersions: MutableMap<String, Any>, /** * The number of free bytes of storage available on the device */ var freeDisk: Long?, /** * The number of free bytes of memory available on the device */ var freeMemory: Long?, /** * The orientation of the device when the event occurred: either portrait or landscape */ var orientation: String?, /** * The timestamp on the device when the event occurred */ var time: Date? ) LongParameterList:EventFilenameInfo.kt$EventFilenameInfo.Companion$( obj: Any, uuid: String = UUID.randomUUID().toString(), apiKey: String?, timestamp: Long = System.currentTimeMillis(), config: ImmutableConfig, isLaunching: Boolean? = null ) LongParameterList:EventStorageModule.kt$EventStorageModule$( contextModule: ContextModule, configModule: ConfigModule, dataCollectionModule: DataCollectionModule, bgTaskService: BackgroundTaskService, trackerModule: TrackerModule, systemServiceModule: SystemServiceModule, notifier: Notifier ) 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 ) - LongParameterList:ThreadState.kt$ThreadState$( stackTraces: MutableMap<java.lang.Thread, Array<StackTraceElement>>, currentThread: java.lang.Thread, exc: Throwable?, isUnhandled: Boolean, projectPackages: Collection<String>, logger: Logger ) + LongParameterList:ThreadState.kt$ThreadState$( stackTraces: MutableMap<java.lang.Thread, Array<StackTraceElement>>, currentThread: java.lang.Thread, exc: Throwable?, isUnhandled: Boolean, projectPackages: Collection<String>, logger: Logger ) MagicNumber:DefaultDelivery.kt$DefaultDelivery$299 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)" - ProtectedMemberInFinalClass:ConfigInternal.kt$ConfigInternal$protected val plugins = HashSet<Plugin>() + 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 ProtectedMemberInFinalClass:EventInternal.kt$EventInternal$protected fun updateSeverityInternal(severity: Severity) ProtectedMemberInFinalClass:EventInternal.kt$EventInternal$protected fun updateSeverityReason(@SeverityReason.SeverityReasonType reason: String) - ReturnCount:DefaultDelivery.kt$DefaultDelivery$fun deliver( urlString: String, streamable: JsonStream.Streamable, headers: Map<String, String?> ): DeliveryStatus + ReturnCount:DefaultDelivery.kt$DefaultDelivery$fun deliver( urlString: String, streamable: JsonStream.Streamable, headers: Map<String, String?> ): DeliveryStatus SwallowedException:ConnectivityCompat.kt$ConnectivityLegacy$catch (e: NullPointerException) { // in some rare cases we get a remote NullPointerException via Parcel.readException null } SwallowedException:ContextExtensions.kt$catch (exc: RuntimeException) { null } SwallowedException:DeviceDataCollector.kt$DeviceDataCollector$catch (exc: Exception) { false } 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..ef9030c005 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 @@ -19,9 +19,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 +82,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 +131,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(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-android-anr/detekt-baseline.xml b/bugsnag-plugin-android-anr/detekt-baseline.xml index 66164023ce..aaaf08230e 100644 --- a/bugsnag-plugin-android-anr/detekt-baseline.xml +++ b/bugsnag-plugin-android-anr/detekt-baseline.xml @@ -1,10 +1,10 @@ - + - + SwallowedException:AnrDetailsCollector.kt$AnrDetailsCollector$catch (exc: RuntimeException) { null } SwallowedException:AnrPlugin.kt$AnrPlugin$catch (exc: Throwable) { null } ThrowingExceptionsWithoutMessageOrCause:AnrPlugin.kt$AnrPlugin$RuntimeException() - UnusedPrivateMember:AnrPlugin.kt$AnrPlugin$ private fun notifyAnrDetected(nativeTrace: List<NativeStackframe>) + UnusedPrivateMember:AnrPlugin.kt$AnrPlugin$ private fun notifyAnrDetected(nativeTrace: List<NativeStackframe>) diff --git a/bugsnag-plugin-android-ndk/detekt-baseline.xml b/bugsnag-plugin-android-ndk/detekt-baseline.xml index ab89d21317..2e00a01a4a 100644 --- a/bugsnag-plugin-android-ndk/detekt-baseline.xml +++ b/bugsnag-plugin-android-ndk/detekt-baseline.xml @@ -1,6 +1,6 @@ - + - + ComplexMethod:NativeBridge.kt$NativeBridge$override fun onStateChange(event: StateEvent) LongParameterList:NativeBridge.kt$NativeBridge$( apiKey: String, reportingDirectory: String, lastRunInfoPath: String, consecutiveLaunchCrashes: Int, autoDetectNdkCrashes: Boolean, apiLevel: Int, is32bit: Boolean, appVersion: String, buildUuid: String, releaseStage: String ) 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