Skip to content

Commit

Permalink
v5.9.4
Browse files Browse the repository at this point in the history
  • Loading branch information
fractalwrench authored May 26, 2021
2 parents 6461462 + aa2858c commit f9f5413
Show file tree
Hide file tree
Showing 46 changed files with 950 additions and 225 deletions.
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,22 @@
# Changelog

## 5.9.4 (2021-05-26)

* Unity: add methods for setting autoNotify and autoDetectAnrs
[#1233](https://github.com/bugsnag/bugsnag-android/pull/1233)

* Including bugsnag.h in C++ code will no longer cause writable-strings warnings
[1260](https://github.com/bugsnag/bugsnag-android/pull/1260)

* Small performance improvements to device and app state collection
[1258](https://github.com/bugsnag/bugsnag-android/pull/1258)

* NDK: lowMemory attribute is now reported as expected
[1262](https://github.com/bugsnag/bugsnag-android/pull/1262)

* Don't include loglog.so in ndk plugin builds performed on Linux
[1263](https://github.com/bugsnag/bugsnag-android/pull/1263)

## 5.9.3 (2021-05-18)

* Avoid unnecessary collection of Thread stacktraces
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package com.bugsnag.android;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import com.bugsnag.android.ObserverInterfaceTest.BugsnagTestObserver;

import android.content.ComponentCallbacks;
import android.content.Context;
import androidx.test.core.app.ApplicationProvider;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Spy;
import org.mockito.junit.MockitoJUnitRunner;

/**
* Heavily mocked test to ensure that onLowMemory events are distributed to Client Observers
*/
@RunWith(MockitoJUnitRunner.class)
public class MemoryTrimTest {

@Spy
Context context = ApplicationProvider.getApplicationContext();

@Captor
ArgumentCaptor<ComponentCallbacks> componentCallbacksCaptor;

@Test
public void onLowMemoryEvent() {
when(context.getApplicationContext()).thenReturn(context);
Client client = new Client(context, BugsnagTestUtils.generateConfiguration());

// capture the registered ComponentCallbacks
verify(context, times(1)).registerComponentCallbacks(componentCallbacksCaptor.capture());

BugsnagTestObserver observer = new BugsnagTestObserver();
client.registerObserver(observer);

ComponentCallbacks callbacks = componentCallbacksCaptor.getValue();
callbacks.onLowMemory();

assertEquals(1, observer.observed.size());
Object observedEvent = observer.observed.get(0);

assertTrue(
"observed event should be UpdateMemoryTrimEvent",
observedEvent instanceof StateEvent.UpdateMemoryTrimEvent
);

assertTrue(
"observed event should be marked isLowMemory",
((StateEvent.UpdateMemoryTrimEvent) observedEvent).isLowMemory()
);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ private <T extends StateEvent> T findMessageInQueue(Class<T> argClass) {
}

static class BugsnagTestObserver implements Observer {
private final ArrayList<Object> observed;
final ArrayList<Object> observed;

BugsnagTestObserver() {
observed = new ArrayList<>(4);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.os.Build
import android.os.SystemClock
import java.util.HashMap

/**
* Collects various data on the application state
Expand All @@ -31,13 +30,19 @@ internal class AppDataCollector(
private val releaseStage = config.releaseStage
private val versionName = config.appVersion ?: packageInfo?.versionName

fun generateApp(): App = App(config, binaryArch, packageName, releaseStage, versionName, codeBundleId)
fun generateApp(): App =
App(config, binaryArch, packageName, releaseStage, versionName, codeBundleId)

fun generateAppWithState(): AppWithState = AppWithState(
config, binaryArch, packageName, releaseStage, versionName, codeBundleId,
getDurationMs(), calculateDurationInForeground(), sessionTracker.isInForeground,
launchCrashTracker.isLaunching()
)
fun generateAppWithState(): AppWithState {
val inForeground = sessionTracker.isInForeground
val durationInForeground = calculateDurationInForeground(inForeground)

return AppWithState(
config, binaryArch, packageName, releaseStage, versionName, codeBundleId,
getDurationMs(), durationInForeground, inForeground,
launchCrashTracker.isLaunching()
)
}

fun getAppDataMetadata(): MutableMap<String, Any?> {
val map = HashMap<String, Any?>()
Expand Down Expand Up @@ -102,9 +107,21 @@ internal class AppDataCollector(
*
* @return the duration in ms
*/
internal fun calculateDurationInForeground(): Long? {
internal fun calculateDurationInForeground(inForeground: Boolean? = sessionTracker.isInForeground): Long? {
if (inForeground == null) {
return null
}

val nowMs = System.currentTimeMillis()
return sessionTracker.getDurationInForegroundMs(nowMs)
var durationMs: Long = 0

val sessionStartTimeMs: Long = sessionTracker.lastEnteredForegroundMs

if (inForeground && sessionStartTimeMs != 0L) {
durationMs = nowMs - sessionStartTimeMs
}

return if (durationMs > 0) durationMs else 0
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,11 @@ public Date getTimestamp() {
return impl.getTimestamp();
}

@NonNull
String getStringTimestamp() {
return DateUtils.toIso8601(impl.getTimestamp());
}

@Override
public void toStream(@NonNull JsonStream stream) throws IOException {
impl.toStream(stream);
Expand Down
60 changes: 50 additions & 10 deletions bugsnag-android-core/src/main/java/com/bugsnag/android/Client.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import androidx.annotation.VisibleForTesting;

import kotlin.Unit;
import kotlin.jvm.functions.Function1;
import kotlin.jvm.functions.Function2;

import java.util.ArrayList;
Expand Down Expand Up @@ -83,7 +84,7 @@ public class Client implements MetadataAware, CallbackAware, UserAware {
final Logger logger;
final DeliveryDelegate deliveryDelegate;

final ClientObservable clientObservable = new ClientObservable();
final ClientObservable clientObservable;
private PluginClient pluginClient;

final Notifier notifier = new Notifier();
Expand All @@ -93,6 +94,7 @@ public class Client implements MetadataAware, CallbackAware, UserAware {
final LastRunInfoStore lastRunInfoStore;
final LaunchCrashTracker launchCrashTracker;
final BackgroundTaskService bgTaskService = new BackgroundTaskService();
private final ExceptionHandler exceptionHandler;

/**
* Initialize a Bugsnag client
Expand Down Expand Up @@ -142,6 +144,7 @@ public Unit invoke(Boolean hasConnection, String networkState) {
immutableConfig = sanitiseConfiguration(appContext, configuration, connectivity);
logger = immutableConfig.getLogger();
warnIfNotAppContext(androidContext);
clientObservable = new ClientObservable();

// Set up breadcrumbs
callbackState = configuration.impl.callbackState.copy();
Expand Down Expand Up @@ -213,14 +216,16 @@ public Unit invoke(String activity, Map<String, ?> metadata) {
immutableConfig, breadcrumbState, notifier, bgTaskService);

// Install a default exception handler with this client
exceptionHandler = new ExceptionHandler(this, logger);
if (immutableConfig.getEnabledErrorTypes().getUnhandledExceptions()) {
new ExceptionHandler(this, logger);
exceptionHandler.install();
}

// register a receiver for automatic breadcrumbs
systemBroadcastReceiver = SystemBroadcastReceiver.register(this, logger, bgTaskService);

registerOrientationChangeListener();
registerMemoryTrimListener();

// load last run info
lastRunInfoStore = new LastRunInfoStore(immutableConfig);
Expand Down Expand Up @@ -249,6 +254,7 @@ public Unit invoke(String activity, Map<String, ?> metadata) {
ContextState contextState,
CallbackState callbackState,
UserState userState,
ClientObservable clientObservable,
Context appContext,
@NonNull DeviceDataCollector deviceDataCollector,
@NonNull AppDataCollector appDataCollector,
Expand All @@ -264,13 +270,15 @@ public Unit invoke(String activity, Map<String, ?> metadata) {
Logger logger,
DeliveryDelegate deliveryDelegate,
LastRunInfoStore lastRunInfoStore,
LaunchCrashTracker launchCrashTracker
LaunchCrashTracker launchCrashTracker,
ExceptionHandler exceptionHandler
) {
this.immutableConfig = immutableConfig;
this.metadataState = metadataState;
this.contextState = contextState;
this.callbackState = callbackState;
this.userState = userState;
this.clientObservable = clientObservable;
this.appContext = appContext;
this.deviceDataCollector = deviceDataCollector;
this.appDataCollector = appDataCollector;
Expand All @@ -288,6 +296,7 @@ public Unit invoke(String activity, Map<String, ?> metadata) {
this.lastRunInfoStore = lastRunInfoStore;
this.launchCrashTracker = launchCrashTracker;
this.lastRunInfo = null;
this.exceptionHandler = exceptionHandler;
}

private LastRunInfo loadLastRunInfo() {
Expand Down Expand Up @@ -350,6 +359,18 @@ public Unit invoke(String oldOrientation, String newOrientation) {
ContextExtensionsKt.registerReceiverSafe(appContext, receiver, configFilter, logger);
}

private void registerMemoryTrimListener() {
appContext.registerComponentCallbacks(new ClientComponentCallbacks(
new Function1<Boolean, Unit>() {
@Override
public Unit invoke(Boolean isLowMemory) {
clientObservable.postMemoryTrimEvent(isLowMemory);
return null;
}
}
));
}

void setupNdkPlugin() {
String lastRunInfoPath = lastRunInfoStore.getFile().getAbsolutePath();
int crashes = (lastRunInfo != null) ? lastRunInfo.getConsecutiveLaunchCrashes() : 0;
Expand All @@ -369,6 +390,17 @@ void registerObserver(Observer observer) {
launchCrashTracker.addObserver(observer);
}

void unregisterObserver(Observer observer) {
metadataState.deleteObserver(observer);
breadcrumbState.deleteObserver(observer);
sessionTracker.deleteObserver(observer);
clientObservable.deleteObserver(observer);
userState.deleteObserver(observer);
contextState.deleteObserver(observer);
deliveryDelegate.deleteObserver(observer);
launchCrashTracker.deleteObserver(observer);
}

/**
* Sends initial state values for Metadata/User/Context to any registered observers.
*/
Expand Down Expand Up @@ -990,13 +1022,7 @@ Logger getLogger() {
@SuppressWarnings("rawtypes")
@Nullable
Plugin getPlugin(@NonNull Class clz) {
Set<Plugin> plugins = pluginClient.getPlugins();
for (Plugin plugin : plugins) {
if (plugin.getClass().equals(clz)) {
return plugin;
}
}
return null;
return pluginClient.findPlugin(clz);
}

Notifier getNotifier() {
Expand All @@ -1006,4 +1032,18 @@ Notifier getNotifier() {
MetadataState getMetadataState() {
return metadataState;
}

void setAutoNotify(boolean autoNotify) {
pluginClient.setAutoNotify(this, autoNotify);

if (autoNotify) {
exceptionHandler.install();
} else {
exceptionHandler.uninstall();
}
}

void setAutoDetectAnrs(boolean autoDetectAnrs) {
pluginClient.setAutoDetectAnrs(this, autoDetectAnrs);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.bugsnag.android

import android.content.ComponentCallbacks
import android.content.res.Configuration

internal class ClientComponentCallbacks(
val callback: (Boolean) -> Unit
) : ComponentCallbacks {
override fun onConfigurationChanged(newConfig: Configuration) {}

override fun onLowMemory() {
callback(true)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ internal class ClientObservable : BaseObservable() {
notifyObservers(StateEvent.UpdateOrientation(orientation))
}

fun postMemoryTrimEvent(isLowMemory: Boolean) {
notifyObservers(StateEvent.UpdateMemoryTrimEvent(isLowMemory))
}

fun postNdkInstall(conf: ImmutableConfig, lastRunInfoPath: String, consecutiveLaunchCrashes: Int) {
notifyObservers(
StateEvent.Install(
Expand Down
Loading

0 comments on commit f9f5413

Please sign in to comment.