Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v5.14.0 #1392

Merged
merged 8 commits into from
Sep 29, 2021
Merged

v5.14.0 #1392

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Changelog

## 5.14.0 (2021-09-29)

### Enhancements

* Capture and report thread state (running, sleeping, etc.) for Android Runtime and Native threads
[#1367](https://github.com/bugsnag/bugsnag-android/pull/1367)
[#1390](https://github.com/bugsnag/bugsnag-android/pull/1390)

## 5.13.0 (2021-09-22)

* Capture breadcrumbs for OkHttp network requests
Expand All @@ -18,7 +26,7 @@
[#1375](https://github.com/bugsnag/bugsnag-android/pull/1375)

* Use SystemClock.elapsedRealtime to track `app.durationInForeground`
[#1375](https://github.com/bugsnag/bugsnag-android/pull/1375)
[#1378](https://github.com/bugsnag/bugsnag-android/pull/1378)

## 5.12.0 (2021-08-26)

Expand Down
4 changes: 3 additions & 1 deletion bugsnag-android-core/detekt-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,15 @@
<ID>LongParameterList:DeviceWithState.kt$DeviceWithState$( buildInfo: DeviceBuildInfo, jailbroken: Boolean?, id: String?, locale: String?, totalMemory: Long?, runtimeVersions: MutableMap&lt;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? )</ID>
<ID>LongParameterList:EventFilenameInfo.kt$EventFilenameInfo.Companion$( obj: Any, uuid: String = UUID.randomUUID().toString(), apiKey: String?, timestamp: Long = System.currentTimeMillis(), config: ImmutableConfig, isLaunching: Boolean? = null )</ID>
<ID>LongParameterList:EventStorageModule.kt$EventStorageModule$( contextModule: ContextModule, configModule: ConfigModule, dataCollectionModule: DataCollectionModule, bgTaskService: BackgroundTaskService, trackerModule: TrackerModule, systemServiceModule: SystemServiceModule, notifier: Notifier )</ID>
<ID>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 )</ID>
<ID>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 )</ID>
<ID>LongParameterList:ThreadState.kt$ThreadState$( stackTraces: MutableMap&lt;java.lang.Thread, Array&lt;StackTraceElement>>, currentThread: java.lang.Thread, exc: Throwable?, isUnhandled: Boolean, projectPackages: Collection&lt;String>, logger: Logger )</ID>
<ID>MagicNumber:DefaultDelivery.kt$DefaultDelivery$299</ID>
<ID>MagicNumber:DefaultDelivery.kt$DefaultDelivery$429</ID>
<ID>MagicNumber:DefaultDelivery.kt$DefaultDelivery$499</ID>
<ID>MagicNumber:LastRunInfoStore.kt$LastRunInfoStore$3</ID>
<ID>MaxLineLength:EventSerializationTest.kt$EventSerializationTest.Companion$it.threads.add(Thread(5, "main", ThreadType.ANDROID, true, Thread.State.RUNNABLE, stacktrace, NoopLogger))</ID>
<ID>MaxLineLength:LastRunInfo.kt$LastRunInfo$return "LastRunInfo(consecutiveLaunchCrashes=$consecutiveLaunchCrashes, crashed=$crashed, crashedDuringLaunch=$crashedDuringLaunch)"</ID>
<ID>MaxLineLength:ThreadState.kt$ThreadState$Thread(thread.id, thread.name, ThreadType.ANDROID, errorThread, Thread.State.forThread(thread), stacktrace, logger)</ID>
<ID>ProtectedMemberInFinalClass:ConfigInternal.kt$ConfigInternal$protected val plugins = HashSet&lt;Plugin>()</ID>
<ID>ProtectedMemberInFinalClass:EventInternal.kt$EventInternal$protected fun isAnr(event: Event): Boolean</ID>
<ID>ProtectedMemberInFinalClass:EventInternal.kt$EventInternal$protected fun shouldDiscardClass(): Boolean</ID>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ public class Client implements MetadataAware, CallbackAware, UserAware {
final ClientObservable clientObservable;
PluginClient pluginClient;

final Notifier notifier = new Notifier();
final Notifier notifier;

@Nullable
final LastRunInfo lastRunInfo;
Expand Down Expand Up @@ -117,6 +117,8 @@ public Client(@NonNull Context androidContext, @NonNull final Configuration conf
ContextModule contextModule = new ContextModule(androidContext);
appContext = contextModule.getCtx();

notifier = configuration.getNotifier();

connectivity = new ConnectivityCompat(appContext, new Function2<Boolean, String, Unit>() {
@Override
public Unit invoke(Boolean hasConnection, String networkState) {
Expand Down Expand Up @@ -233,7 +235,8 @@ public Unit invoke(Boolean hasConnection, String networkState) {
DeliveryDelegate deliveryDelegate,
LastRunInfoStore lastRunInfoStore,
LaunchCrashTracker launchCrashTracker,
ExceptionHandler exceptionHandler
ExceptionHandler exceptionHandler,
Notifier notifier
) {
this.immutableConfig = immutableConfig;
this.metadataState = metadataState;
Expand All @@ -255,6 +258,7 @@ public Unit invoke(Boolean hasConnection, String networkState) {
this.launchCrashTracker = launchCrashTracker;
this.lastRunInfo = null;
this.exceptionHandler = exceptionHandler;
this.notifier = notifier;
}

void registerLifecycleCallbacks() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ internal class ClientObservable : BaseObservable() {
conf.buildUuid,
conf.releaseStage,
lastRunInfoPath,
consecutiveLaunchCrashes
consecutiveLaunchCrashes,
conf.sendThreads
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ internal class ConfigInternal(var apiKey: String) : CallbackAware, MetadataAware
var projectPackages: Set<String> = emptySet()
var persistenceDirectory: File? = null

val notifier: Notifier = Notifier()

protected val plugins = HashSet<Plugin>()

override fun addOnError(onError: OnErrorCallback) = callbackState.addOnError(onError)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -981,4 +981,8 @@ public void addPlugin(@NonNull Plugin plugin) {
Set<Plugin> getPlugins() {
return impl.getPlugins();
}

Notifier getNotifier() {
return impl.getNotifier();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import java.io.IOException
*/
class Notifier @JvmOverloads constructor(
var name: String = "Android Bugsnag Notifier",
var version: String = "5.13.0",
var version: String = "5.14.0",
var url: String = "https://bugsnag.com"
) : JsonStream.Streamable {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ sealed class StateEvent { // JvmField allows direct field access optimizations
@JvmField val buildUuid: String?,
@JvmField val releaseStage: String?,
@JvmField val lastRunInfoPath: String,
@JvmField val consecutiveLaunchCrashes: Int
@JvmField val consecutiveLaunchCrashes: Int,
@JvmField val sendThreads: ThreadSendPolicy
) : StateEvent()

object DeliverPending : StateEvent()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.bugsnag.android;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import java.io.IOException;
import java.util.List;
Expand All @@ -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;
}

Expand Down Expand Up @@ -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
*/
Expand Down Expand Up @@ -111,4 +132,79 @@ public List<Stackframe> 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("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) {
java.lang.Thread.State state = thread.getState();
return getState(state);
}

/**
* 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 descriptor a consistent descriptor of the state constant to lookup
* @return the requested {@link State} or {@link #UNKNOWN}
*/
@NonNull
public static State byDescriptor(@Nullable String descriptor) {
if (descriptor == null) {
return UNKNOWN;
}

for (State state : values()) {
if (state.getDescriptor().equals(descriptor)) {
return state;
}
}

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;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand All @@ -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.descriptor)

writer.name("stacktrace")
writer.beginArray()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@ public class ClientFacadeTest {
@Mock
ExceptionHandler exceptionHandler;

@Mock
Notifier notifier;

private Client client;
private InterceptingLogger logger;

Expand Down Expand Up @@ -118,7 +121,8 @@ public void setUp() {
deliveryDelegate,
lastRunInfoStore,
launchCrashTracker,
exceptionHandler
exceptionHandler,
notifier
);

// required fields for generating an event
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,15 @@ public void setUp() {
logger = new InterceptingLogger();
List<Stackframe> 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ internal class ThreadSerializationTest {
"main-one",
ThreadType.ANDROID,
true,
Thread.State.RUNNABLE,
Stacktrace(
stacktrace,
emptySet(),
Expand All @@ -43,6 +44,7 @@ internal class ThreadSerializationTest {
"main-one",
ThreadType.ANDROID,
false,
Thread.State.RUNNABLE,
Stacktrace(
stacktrace1,
emptySet(),
Expand Down Expand Up @@ -76,6 +78,7 @@ internal class ThreadSerializationTest {
"main-one",
ThreadType.ANDROID,
true,
Thread.State.RUNNABLE,
trace,
NoopLogger
)
Expand All @@ -98,6 +101,7 @@ internal class ThreadSerializationTest {
"main-one",
ThreadType.ANDROID,
false,
Thread.State.RUNNABLE,
trace,
NoopLogger
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"id": 5,
"name": "main",
"type": "android",
"state": "RUNNABLE",
"stacktrace": [],
"errorReportingThread": true
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"id": 24,
"name": "main-one",
"type": "android",
"state": "RUNNABLE",
"stacktrace": [
{
"method": "run_func",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"id": 24,
"name": "main-one",
"type": "android",
"state": "RUNNABLE",
"stacktrace": [
{
"method": "run_func",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"id": 24,
"name": "main-one",
"type": "android",
"state": "RUNNABLE",
"stacktrace": [
{
"method": "run_func",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"id": 24,
"name": "main-one",
"type": "android",
"state": "RUNNABLE",
"stacktrace": [
{
"method": "run_func",
Expand Down
Loading