Updated Bugsnag to version 5.11.0

This commit is contained in:
M66B 2021-08-15 14:40:22 +02:00
parent ab1ae4220a
commit a901aee4b7
28 changed files with 563 additions and 143 deletions

View File

@ -291,7 +291,7 @@ dependencies {
def dnsjava_version = "2.1.9"
def openpgp_version = "12.0"
def badge_version = "1.1.22"
def bugsnag_version = "5.9.4"
def bugsnag_version = "5.11.0"
def biweekly_version = "0.6.6"
def relinker_version = "1.4.3"
def markwon_version = "4.6.2"

View File

@ -0,0 +1,36 @@
package com.bugsnag.android
import com.bugsnag.android.internal.dag.ConfigModule
import com.bugsnag.android.internal.dag.DependencyModule
/**
* A dependency module which constructs the objects that track state in Bugsnag. For example, this
* class is responsible for creating classes which track the current breadcrumb/metadata state.
*/
internal class BugsnagStateModule(
configModule: ConfigModule,
configuration: Configuration
) : DependencyModule() {
private val cfg = configModule.config
val clientObservable = ClientObservable()
val callbackState = configuration.impl.callbackState.copy()
val contextState = ContextState().apply {
if (configuration.context != null) {
setManualContext(configuration.context)
}
}
val breadcrumbState = BreadcrumbState(cfg.maxBreadcrumbs, callbackState, cfg.logger)
val metadataState = copyMetadataState(configuration)
private fun copyMetadataState(configuration: Configuration): MetadataState {
// performs deep copy of metadata to preserve immutability of Configuration interface
val orig = configuration.impl.metadataState.metadata
return configuration.impl.metadataState.copy(metadata = orig.copy())
}
}

View File

@ -0,0 +1,52 @@
package com.bugsnag.android;
import android.os.Build;
import android.os.StrictMode.OnThreadViolationListener;
import android.os.strictmode.Violation;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
/**
* Sends an error report to Bugsnag for each StrictMode thread policy violation that occurs in
* your app.
* <p></p>
* You should use this class by instantiating Bugsnag in the normal way and then set the
* StrictMode policy with
* {@link android.os.StrictMode.ThreadPolicy.Builder#penaltyListener
* (Executor, OnThreadViolationListener)}.
* This functionality is only supported on API 28+.
*/
@RequiresApi(api = Build.VERSION_CODES.P)
public class BugsnagThreadViolationListener implements OnThreadViolationListener {
private final Client client;
private final OnThreadViolationListener listener;
public BugsnagThreadViolationListener() {
this(Bugsnag.getClient(), null);
}
public BugsnagThreadViolationListener(@NonNull Client client) {
this(client, null);
}
public BugsnagThreadViolationListener(@NonNull Client client,
@Nullable OnThreadViolationListener listener) {
this.client = client;
this.listener = listener;
}
@Override
public void onThreadViolation(@NonNull Violation violation) {
if (client != null) {
client.notify(violation, new StrictModeOnErrorCallback(
"StrictMode policy violation detected: ThreadPolicy"
));
}
if (listener != null) {
listener.onThreadViolation(violation);
}
}
}

View File

@ -0,0 +1,52 @@
package com.bugsnag.android;
import android.os.Build;
import android.os.StrictMode.OnVmViolationListener;
import android.os.strictmode.Violation;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
/**
* Sends an error report to Bugsnag for each StrictMode VM policy violation that occurs in
* your app.
* <p></p>
* You should use this class by instantiating Bugsnag in the normal way and then set the
* StrictMode policy with
* {@link android.os.StrictMode.VmPolicy.Builder#penaltyListener
* (Executor, OnVmViolationListener)}.
* This functionality is only supported on API 28+.
*/
@RequiresApi(api = Build.VERSION_CODES.P)
public class BugsnagVmViolationListener implements OnVmViolationListener {
private final Client client;
private final OnVmViolationListener listener;
public BugsnagVmViolationListener() {
this(Bugsnag.getClient(), null);
}
public BugsnagVmViolationListener(@NonNull Client client) {
this(client, null);
}
public BugsnagVmViolationListener(@NonNull Client client,
@Nullable OnVmViolationListener listener) {
this.client = client;
this.listener = listener;
}
@Override
public void onVmViolation(@NonNull Violation violation) {
if (client != null) {
client.notify(violation, new StrictModeOnErrorCallback(
"StrictMode policy violation detected: VmPolicy"
));
}
if (listener != null) {
listener.onVmViolation(violation);
}
}
}

View File

@ -1,19 +1,15 @@
package com.bugsnag.android;
import static com.bugsnag.android.ContextExtensionsKt.getActivityManagerFrom;
import static com.bugsnag.android.ContextExtensionsKt.getStorageManagerFrom;
import static com.bugsnag.android.SeverityReason.REASON_HANDLED_EXCEPTION;
import static com.bugsnag.android.internal.ImmutableConfigKt.sanitiseConfiguration;
import com.bugsnag.android.internal.ImmutableConfig;
import com.bugsnag.android.internal.StateObserver;
import com.bugsnag.android.internal.dag.ConfigModule;
import com.bugsnag.android.internal.dag.ContextModule;
import com.bugsnag.android.internal.dag.SystemServiceModule;
import android.app.ActivityManager;
import android.app.Application;
import android.content.Context;
import android.content.res.Resources;
import android.os.Environment;
import android.os.storage.StorageManager;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@ -23,12 +19,14 @@ import kotlin.Unit;
import kotlin.jvm.functions.Function1;
import kotlin.jvm.functions.Function2;
import java.io.File;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.RejectedExecutionException;
/**
@ -67,24 +65,16 @@ public class Client implements MetadataAware, CallbackAware, UserAware {
@NonNull
protected final EventStore eventStore;
private final SessionStore sessionStore;
final SessionTracker sessionTracker;
final SystemBroadcastReceiver systemBroadcastReceiver;
private final ActivityBreadcrumbCollector activityBreadcrumbCollector;
private final SessionLifecycleCallback sessionLifecycleCallback;
final Connectivity connectivity;
@Nullable
private final StorageManager storageManager;
final Logger logger;
final Connectivity connectivity;
final DeliveryDelegate deliveryDelegate;
final ClientObservable clientObservable;
private PluginClient pluginClient;
PluginClient pluginClient;
final Notifier notifier = new Notifier();
@ -121,8 +111,8 @@ public class Client implements MetadataAware, CallbackAware, UserAware {
* @param configuration a configuration for the Client
*/
public Client(@NonNull Context androidContext, @NonNull final Configuration configuration) {
Context ctx = androidContext.getApplicationContext();
appContext = ctx != null ? ctx : androidContext;
ContextModule contextModule = new ContextModule(androidContext);
appContext = contextModule.getCtx();
connectivity = new ConnectivityCompat(appContext, new Function2<Boolean, String, Unit>() {
@Override
@ -140,78 +130,53 @@ public class Client implements MetadataAware, CallbackAware, UserAware {
});
// set sensible defaults for delivery/project packages etc if not set
immutableConfig = sanitiseConfiguration(appContext, configuration, connectivity);
ConfigModule configModule = new ConfigModule(contextModule, configuration, connectivity);
immutableConfig = configModule.getConfig();
logger = immutableConfig.getLogger();
warnIfNotAppContext(androidContext);
clientObservable = new ClientObservable();
// Set up breadcrumbs
callbackState = configuration.impl.callbackState.copy();
int maxBreadcrumbs = immutableConfig.getMaxBreadcrumbs();
breadcrumbState = new BreadcrumbState(maxBreadcrumbs, callbackState, logger);
// setup storage as soon as possible
final StorageModule storageModule = new StorageModule(appContext,
immutableConfig, logger);
storageManager = getStorageManagerFrom(appContext);
contextState = new ContextState();
// setup state trackers for bugsnag
BugsnagStateModule bugsnagStateModule = new BugsnagStateModule(
configModule, configuration);
clientObservable = bugsnagStateModule.getClientObservable();
callbackState = bugsnagStateModule.getCallbackState();
breadcrumbState = bugsnagStateModule.getBreadcrumbState();
contextState = bugsnagStateModule.getContextState();
metadataState = bugsnagStateModule.getMetadataState();
if (configuration.getContext() != null) {
contextState.setManualContext(configuration.getContext());
}
// lookup system services
final SystemServiceModule systemServiceModule = new SystemServiceModule(contextModule);
sessionStore = new SessionStore(immutableConfig, logger, null);
sessionTracker = new SessionTracker(immutableConfig, callbackState, this,
sessionStore, logger, bgTaskService);
metadataState = copyMetadataState(configuration);
// block until storage module has resolved everything
storageModule.resolveDependencies(bgTaskService, TaskType.IO);
ActivityManager am = getActivityManagerFrom(appContext);
// setup further state trackers and data collection
TrackerModule trackerModule = new TrackerModule(configModule,
storageModule, this, bgTaskService, callbackState);
launchCrashTracker = trackerModule.getLaunchCrashTracker();
sessionTracker = trackerModule.getSessionTracker();
launchCrashTracker = new LaunchCrashTracker(immutableConfig);
appDataCollector = new AppDataCollector(appContext, appContext.getPackageManager(),
immutableConfig, sessionTracker, am, launchCrashTracker, logger);
DataCollectionModule dataCollectionModule = new DataCollectionModule(contextModule,
configModule, systemServiceModule, trackerModule,
bgTaskService, connectivity, storageModule.getDeviceId());
dataCollectionModule.resolveDependencies(bgTaskService, TaskType.IO);
appDataCollector = dataCollectionModule.getAppDataCollector();
deviceDataCollector = dataCollectionModule.getDeviceDataCollector();
// load the device + user information
SharedPrefMigrator sharedPrefMigrator = new SharedPrefMigrator(appContext);
DeviceIdStore deviceIdStore = new DeviceIdStore(appContext, sharedPrefMigrator, logger);
String deviceId = deviceIdStore.loadDeviceId();
UserStore userStore = new UserStore(immutableConfig, deviceId, sharedPrefMigrator, logger);
userState = userStore.load(configuration.getUser());
sharedPrefMigrator.deleteLegacyPrefs();
userState = storageModule.getUserStore().load(configuration.getUser());
storageModule.getSharedPrefMigrator().deleteLegacyPrefs();
DeviceBuildInfo info = DeviceBuildInfo.Companion.defaultInfo();
Resources resources = appContext.getResources();
deviceDataCollector = new DeviceDataCollector(connectivity, appContext,
resources, deviceId, info, Environment.getDataDirectory(),
new RootDetector(logger), bgTaskService, logger);
registerLifecycleCallbacks();
if (appContext instanceof Application) {
Application application = (Application) appContext;
sessionLifecycleCallback = new SessionLifecycleCallback(sessionTracker);
application.registerActivityLifecycleCallbacks(sessionLifecycleCallback);
if (!immutableConfig.shouldDiscardBreadcrumb(BreadcrumbType.STATE)) {
this.activityBreadcrumbCollector = new ActivityBreadcrumbCollector(
new Function2<String, Map<String, ? extends Object>, Unit>() {
@SuppressWarnings("unchecked")
@Override
public Unit invoke(String activity, Map<String, ?> metadata) {
leaveBreadcrumb(activity, (Map<String, Object>) metadata,
BreadcrumbType.STATE);
return null;
}
}
);
application.registerActivityLifecycleCallbacks(activityBreadcrumbCollector);
} else {
this.activityBreadcrumbCollector = null;
}
} else {
this.activityBreadcrumbCollector = null;
this.sessionLifecycleCallback = null;
}
InternalReportDelegate delegate = new InternalReportDelegate(appContext, logger,
immutableConfig, storageManager, appDataCollector, deviceDataCollector,
sessionTracker, notifier, bgTaskService);
eventStore = new EventStore(immutableConfig, logger, notifier, bgTaskService, delegate);
EventStorageModule eventStorageModule = new EventStorageModule(contextModule, configModule,
dataCollectionModule, bgTaskService, trackerModule, systemServiceModule, notifier);
eventStorageModule.resolveDependencies(bgTaskService, TaskType.IO);
eventStore = eventStorageModule.getEventStore();
deliveryDelegate = new DeliveryDelegate(logger, eventStore,
immutableConfig, breadcrumbState, notifier, bgTaskService);
@ -223,8 +188,8 @@ public class Client implements MetadataAware, CallbackAware, UserAware {
}
// load last run info
lastRunInfoStore = new LastRunInfoStore(immutableConfig);
lastRunInfo = loadLastRunInfo();
lastRunInfoStore = storageModule.getLastRunInfoStore();
lastRunInfo = storageModule.getLastRunInfo();
// initialise plugins before attempting to flush any errors
loadPlugins(configuration);
@ -258,13 +223,9 @@ public class Client implements MetadataAware, CallbackAware, UserAware {
@NonNull AppDataCollector appDataCollector,
@NonNull BreadcrumbState breadcrumbState,
@NonNull EventStore eventStore,
SessionStore sessionStore,
SystemBroadcastReceiver systemBroadcastReceiver,
SessionTracker sessionTracker,
ActivityBreadcrumbCollector activityBreadcrumbCollector,
SessionLifecycleCallback sessionLifecycleCallback,
Connectivity connectivity,
@Nullable StorageManager storageManager,
Logger logger,
DeliveryDelegate deliveryDelegate,
LastRunInfoStore lastRunInfoStore,
@ -282,13 +243,9 @@ public class Client implements MetadataAware, CallbackAware, UserAware {
this.appDataCollector = appDataCollector;
this.breadcrumbState = breadcrumbState;
this.eventStore = eventStore;
this.sessionStore = sessionStore;
this.systemBroadcastReceiver = systemBroadcastReceiver;
this.sessionTracker = sessionTracker;
this.activityBreadcrumbCollector = activityBreadcrumbCollector;
this.sessionLifecycleCallback = sessionLifecycleCallback;
this.connectivity = connectivity;
this.storageManager = storageManager;
this.logger = logger;
this.deliveryDelegate = deliveryDelegate;
this.lastRunInfoStore = lastRunInfoStore;
@ -297,6 +254,29 @@ public class Client implements MetadataAware, CallbackAware, UserAware {
this.exceptionHandler = exceptionHandler;
}
void registerLifecycleCallbacks() {
if (appContext instanceof Application) {
Application application = (Application) appContext;
SessionLifecycleCallback sessionCb = new SessionLifecycleCallback(sessionTracker);
application.registerActivityLifecycleCallbacks(sessionCb);
if (!immutableConfig.shouldDiscardBreadcrumb(BreadcrumbType.STATE)) {
ActivityBreadcrumbCollector activityCb = new ActivityBreadcrumbCollector(
new Function2<String, Map<String, ? extends Object>, Unit>() {
@SuppressWarnings("unchecked")
@Override
public Unit invoke(String activity, Map<String, ?> metadata) {
leaveBreadcrumb(activity, (Map<String, Object>) metadata,
BreadcrumbType.STATE);
return null;
}
}
);
application.registerActivityLifecycleCallbacks(activityCb);
}
}
}
/**
* Registers listeners for system events in the background. This offloads work from the main
* thread that collects useful information from callbacks, but that don't need to be done
@ -316,12 +296,6 @@ public class Client implements MetadataAware, CallbackAware, UserAware {
}
}
private LastRunInfo loadLastRunInfo() {
LastRunInfo lastRunInfo = lastRunInfoStore.load();
LastRunInfo currentRunInfo = new LastRunInfo(0, false, false);
persistRunInfo(currentRunInfo);
return lastRunInfo;
}
/**
* Load information about the last run, and reset the persisted information to the defaults.
@ -339,24 +313,17 @@ public class Client implements MetadataAware, CallbackAware, UserAware {
}
}
private void loadPlugins(@NonNull Configuration configuration) {
NativeInterface.setClient(this);
private void loadPlugins(@NonNull final Configuration configuration) {
NativeInterface.setClient(Client.this);
Set<Plugin> userPlugins = configuration.getPlugins();
pluginClient = new PluginClient(userPlugins, immutableConfig, logger);
pluginClient.loadPlugins(this);
pluginClient.loadPlugins(Client.this);
}
private void logNull(String property) {
logger.e("Invalid null value supplied to client." + property + ", ignoring");
}
private MetadataState copyMetadataState(@NonNull Configuration configuration) {
// performs deep copy of metadata to preserve immutability of Configuration interface
Metadata orig = configuration.impl.metadataState.getMetadata();
Metadata copy = orig.copy();
return configuration.impl.metadataState.copy(copy);
}
private void registerComponentCallbacks() {
appContext.registerComponentCallbacks(new ClientComponentCallbacks(
deviceDataCollector,
@ -381,6 +348,11 @@ public class Client implements MetadataAware, CallbackAware, UserAware {
}
void setupNdkPlugin() {
if (!setupNdkDirectory()) {
logger.w("Failed to setup NDK directory.");
return;
}
String lastRunInfoPath = lastRunInfoStore.getFile().getAbsolutePath();
int crashes = (lastRunInfo != null) ? lastRunInfo.getConsecutiveLaunchCrashes() : 0;
clientObservable.postNdkInstall(immutableConfig, lastRunInfoPath, crashes);
@ -388,6 +360,20 @@ public class Client implements MetadataAware, CallbackAware, UserAware {
clientObservable.postNdkDeliverPending();
}
private boolean setupNdkDirectory() {
try {
return bgTaskService.submitTask(TaskType.IO, new Callable<Boolean>() {
@Override
public Boolean call() {
File outFile = new File(NativeInterface.getNativeReportPath());
return outFile.exists() || outFile.mkdirs();
}
}).get();
} catch (Throwable exc) {
return false;
}
}
void addObserver(StateObserver observer) {
metadataState.addObserver(observer);
breadcrumbState.addObserver(observer);

View File

@ -0,0 +1,58 @@
package com.bugsnag.android
import android.os.Environment
import com.bugsnag.android.internal.dag.ConfigModule
import com.bugsnag.android.internal.dag.ContextModule
import com.bugsnag.android.internal.dag.DependencyModule
import com.bugsnag.android.internal.dag.SystemServiceModule
/**
* A dependency module which constructs the objects that collect data in Bugsnag. For example, this
* class is responsible for creating classes which capture device-specific information.
*/
internal class DataCollectionModule(
contextModule: ContextModule,
configModule: ConfigModule,
systemServiceModule: SystemServiceModule,
trackerModule: TrackerModule,
bgTaskService: BackgroundTaskService,
connectivity: Connectivity,
deviceId: String?
) : DependencyModule() {
private val ctx = contextModule.ctx
private val cfg = configModule.config
private val logger = cfg.logger
private val deviceBuildInfo: DeviceBuildInfo = DeviceBuildInfo.defaultInfo()
private val dataDir = Environment.getDataDirectory()
val appDataCollector by future {
AppDataCollector(
ctx,
ctx.packageManager,
cfg,
trackerModule.sessionTracker,
systemServiceModule.activityManager,
trackerModule.launchCrashTracker,
logger
)
}
private val rootDetector by future {
RootDetector(logger = logger, deviceBuildInfo = deviceBuildInfo)
}
val deviceDataCollector by future {
DeviceDataCollector(
connectivity,
ctx,
ctx.resources,
deviceId,
deviceBuildInfo,
dataDir,
rootDetector,
bgTaskService,
logger
)
}
}

View File

@ -1,5 +1,6 @@
package com.bugsnag.android
import android.net.TrafficStats
import java.io.ByteArrayOutputStream
import java.io.IOException
import java.io.PrintWriter
@ -42,6 +43,7 @@ internal class DefaultDelivery(
headers: Map<String, String?>
): DeliveryStatus {
TrafficStats.setThreadStatsTag(1)
if (connectivity != null && !connectivity.hasNetworkConnection()) {
return DeliveryStatus.UNDELIVERED
}
@ -64,7 +66,7 @@ internal class DefaultDelivery(
return DeliveryStatus.UNDELIVERED
} catch (exception: IOException) {
logger.w("IOException encountered in request", exception)
return DeliveryStatus.FAILURE
return DeliveryStatus.UNDELIVERED
} catch (exception: Exception) {
logger.w("Unexpected error delivering payload", exception)
return DeliveryStatus.FAILURE

View File

@ -28,7 +28,7 @@ internal class DeviceDataCollector(
private val buildInfo: DeviceBuildInfo,
private val dataDirectory: File,
rootDetector: RootDetector,
bgTaskService: BackgroundTaskService,
private val bgTaskService: BackgroundTaskService,
private val logger: Logger
) {
@ -207,7 +207,12 @@ internal class DeviceDataCollector(
fun calculateFreeDisk(): Long {
// for this specific case we want the currently usable space, not
// StorageManager#allocatableBytes() as the UsableSpace lint inspection suggests
return dataDirectory.usableSpace
return runCatching {
bgTaskService.submitTask(
TaskType.IO,
Callable { dataDirectory.usableSpace }
).get()
}.getOrDefault(0L)
}
/**

View File

@ -28,9 +28,7 @@ internal class DeviceIdStore @JvmOverloads constructor(
init {
try {
if (!file.exists()) {
file.createNewFile()
}
file.createNewFile()
} catch (exc: Throwable) {
logger.w("Failed to created device ID file", exc)
}

View File

@ -320,6 +320,10 @@ public class Event implements JsonStream.Streamable, MetadataAware, UserAware {
impl.updateSeverityInternal(severity);
}
protected void updateSeverityReason(@NonNull @SeverityReason.SeverityReasonType String reason) {
impl.updateSeverityReason(reason);
}
void setApp(@NonNull AppWithState app) {
impl.setApp(app);
}

View File

@ -130,12 +130,21 @@ internal class EventInternal @JvmOverloads internal constructor(
}
protected fun updateSeverityInternal(severity: Severity) {
severityReason = SeverityReason.newInstance(
severityReason = SeverityReason(
severityReason.severityReasonType,
severity,
severityReason.unhandled,
severityReason.attributeValue
)
}
protected fun updateSeverityReason(@SeverityReason.SeverityReasonType reason: String) {
severityReason = SeverityReason(
reason,
severityReason.currentSeverity,
severityReason.unhandled,
severityReason.attributeValue
)
this.severity = severity
}
fun getSeverityReasonType(): String = severityReason.severityReasonType

View File

@ -0,0 +1,38 @@
package com.bugsnag.android
import com.bugsnag.android.internal.dag.ConfigModule
import com.bugsnag.android.internal.dag.ContextModule
import com.bugsnag.android.internal.dag.DependencyModule
import com.bugsnag.android.internal.dag.SystemServiceModule
/**
* A dependency module which constructs the objects that persist events to disk in Bugsnag.
*/
internal class EventStorageModule(
contextModule: ContextModule,
configModule: ConfigModule,
dataCollectionModule: DataCollectionModule,
bgTaskService: BackgroundTaskService,
trackerModule: TrackerModule,
systemServiceModule: SystemServiceModule,
notifier: Notifier
) : DependencyModule() {
private val cfg = configModule.config
private val delegate by future {
InternalReportDelegate(
contextModule.ctx,
cfg.logger,
cfg,
systemServiceModule.storageManager,
dataCollectionModule.appDataCollector,
dataCollectionModule.deviceDataCollector,
trackerModule.sessionTracker,
notifier,
bgTaskService
)
}
val eventStore by future { EventStore(cfg, cfg.logger, notifier, bgTaskService, delegate) }
}

View File

@ -11,7 +11,6 @@ import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.RejectedExecutionException;
@ -53,7 +52,7 @@ class EventStore extends FileStore {
Notifier notifier,
BackgroundTaskService bgTaskSevice,
Delegate delegate) {
super(new File(config.getPersistenceDirectory(), "bugsnag-errors"),
super(new File(config.getPersistenceDirectory().getValue(), "bugsnag-errors"),
config.getMaxPersistedEvents(),
EVENT_COMPARATOR,
logger,

View File

@ -63,13 +63,7 @@ abstract class FileStore {
*/
private boolean isStorageDirValid(@NonNull File storageDir) {
try {
if (!storageDir.isDirectory() || !storageDir.canWrite()) {
if (!storageDir.mkdirs()) {
this.logger.e("Could not prepare storage directory at "
+ storageDir.getAbsolutePath());
return false;
}
}
storageDir.mkdirs();
} catch (Exception exception) {
this.logger.e("Could not prepare file storage directory", exception);
return false;

View File

@ -16,7 +16,7 @@ private const val KEY_CRASHED_DURING_LAUNCH = "crashedDuringLaunch"
*/
internal class LastRunInfoStore(config: ImmutableConfig) {
val file: File = File(config.persistenceDirectory, "last-run-info")
val file: File = File(config.persistenceDirectory.value, "last-run-info")
private val logger: Logger = config.logger
private val lock = ReentrantReadWriteLock()

View File

@ -18,17 +18,29 @@ class LibraryLoader {
* @param callback an OnErrorCallback
* @return true if the library was loaded, false if not
*/
boolean loadLibrary(String name, Client client, OnErrorCallback callback) {
boolean loadLibrary(final String name, final Client client, final OnErrorCallback callback) {
try {
client.bgTaskService.submitTask(TaskType.IO, new Runnable() {
@Override
public void run() {
loadLibInternal(name, client, callback);
}
}).get();
return loaded;
} catch (Throwable exc) {
return false;
}
}
void loadLibInternal(String name, Client client, OnErrorCallback callback) {
if (!attemptedLoad.getAndSet(true)) {
try {
System.loadLibrary(name);
loaded = true;
return true;
} catch (UnsatisfiedLinkError error) {
client.notify(error, callback);
}
}
return false;
}
boolean isLoaded() {

View File

@ -56,7 +56,7 @@ public class NativeInterface {
@NonNull
public static String getNativeReportPath() {
ImmutableConfig config = getClient().getConfig();
File persistenceDirectory = config.getPersistenceDirectory();
File persistenceDirectory = config.getPersistenceDirectory().getValue();
return new File(persistenceDirectory, "bugsnag-native").getAbsolutePath();
}

View File

@ -7,7 +7,7 @@ import java.io.IOException
*/
class Notifier @JvmOverloads constructor(
var name: String = "Android Bugsnag Notifier",
var version: String = "5.10.1",
var version: String = "5.11.0",
var url: String = "https://bugsnag.com"
) : JsonStream.Streamable {

View File

@ -7,7 +7,6 @@ import androidx.annotation.Nullable;
import java.io.File;
import java.util.Comparator;
import java.util.Locale;
import java.util.UUID;
/**
@ -37,7 +36,7 @@ class SessionStore extends FileStore {
SessionStore(@NonNull ImmutableConfig config,
@NonNull Logger logger,
@Nullable Delegate delegate) {
super(new File(config.getPersistenceDirectory(), "bugsnag-sessions"),
super(new File(config.getPersistenceDirectory().getValue(), "bugsnag-sessions"),
config.getMaxPersistedSessions(),
SESSION_COMPARATOR,
logger,

View File

@ -0,0 +1,47 @@
package com.bugsnag.android
import android.content.Context
import com.bugsnag.android.internal.ImmutableConfig
import com.bugsnag.android.internal.dag.DependencyModule
/**
* A dependency module which constructs the objects that store information to disk in Bugsnag.
*/
internal class StorageModule(
appContext: Context,
immutableConfig: ImmutableConfig,
logger: Logger
) : DependencyModule() {
val sharedPrefMigrator by future { SharedPrefMigrator(appContext) }
private val deviceIdStore by future {
DeviceIdStore(
appContext,
sharedPrefMigrator = sharedPrefMigrator,
logger = logger
)
}
val deviceId by future { deviceIdStore.loadDeviceId() }
val userStore by future {
UserStore(
immutableConfig,
deviceId,
sharedPrefMigrator = sharedPrefMigrator,
logger = logger
)
}
val lastRunInfoStore by future { LastRunInfoStore(immutableConfig) }
val sessionStore by future { SessionStore(immutableConfig, logger, null) }
val lastRunInfo by future {
val info = lastRunInfoStore.load()
val currentRunInfo = LastRunInfo(0, crashed = false, crashedDuringLaunch = false)
lastRunInfoStore.persist(currentRunInfo)
info
}
}

View File

@ -0,0 +1,11 @@
package com.bugsnag.android
internal class StrictModeOnErrorCallback(private val errMsg: String) : OnErrorCallback {
override fun onError(event: Event): Boolean {
event.updateSeverityInternal(Severity.INFO)
event.updateSeverityReason(SeverityReason.REASON_STRICT_MODE)
val error = event.errors.firstOrNull()
error?.errorMessage = errMsg
return true
}
}

View File

@ -0,0 +1,30 @@
package com.bugsnag.android
import com.bugsnag.android.internal.dag.ConfigModule
import com.bugsnag.android.internal.dag.DependencyModule
/**
* A dependency module which constructs objects that track launch/session related information
* in Bugsnag.
*/
internal class TrackerModule(
configModule: ConfigModule,
storageModule: StorageModule,
client: Client,
bgTaskService: BackgroundTaskService,
callbackState: CallbackState
) : DependencyModule() {
private val config = configModule.config
val launchCrashTracker = LaunchCrashTracker(config)
val sessionTracker = SessionTracker(
config,
callbackState,
client,
storageModule.sessionStore,
config.logger,
bgTaskService
)
}

View File

@ -12,7 +12,7 @@ import java.util.concurrent.atomic.AtomicReference
internal class UserStore @JvmOverloads constructor(
private val config: ImmutableConfig,
private val deviceId: String?,
file: File = File(config.persistenceDirectory, "user-info"),
file: File = File(config.persistenceDirectory.value, "user-info"),
private val sharedPrefMigrator: SharedPrefMigrator,
private val logger: Logger
) {
@ -23,9 +23,7 @@ internal class UserStore @JvmOverloads constructor(
init {
try {
if (!file.exists()) {
file.createNewFile()
}
file.createNewFile()
} catch (exc: IOException) {
logger.w("Failed to created device ID file", exc)
}

View File

@ -47,7 +47,7 @@ data class ImmutableConfig(
val maxBreadcrumbs: Int,
val maxPersistedEvents: Int,
val maxPersistedSessions: Int,
val persistenceDirectory: File,
val persistenceDirectory: Lazy<File>,
val sendLaunchCrashesSynchronously: Boolean,
// results cached here to avoid unnecessary lookups in Client.
@ -128,7 +128,8 @@ internal fun convertToImmutableConfig(
config: Configuration,
buildUuid: String? = null,
packageInfo: PackageInfo? = null,
appInfo: ApplicationInfo? = null
appInfo: ApplicationInfo? = null,
persistenceDir: Lazy<File> = lazy { requireNotNull(config.persistenceDirectory) }
): ImmutableConfig {
val errorTypes = when {
config.autoDetectErrors -> config.enabledErrorTypes.copy()
@ -158,7 +159,7 @@ internal fun convertToImmutableConfig(
maxPersistedEvents = config.maxPersistedEvents,
maxPersistedSessions = config.maxPersistedSessions,
enabledBreadcrumbTypes = config.enabledBreadcrumbTypes?.toSet(),
persistenceDirectory = config.persistenceDirectory!!,
persistenceDirectory = persistenceDir,
sendLaunchCrashesSynchronously = config.sendLaunchCrashesSynchronously,
packageInfo = packageInfo,
appInfo = appInfo
@ -214,11 +215,13 @@ internal fun sanitiseConfiguration(
if (configuration.delivery == null) {
configuration.delivery = DefaultDelivery(connectivity, configuration.logger!!)
}
if (configuration.persistenceDirectory == null) {
configuration.persistenceDirectory = appContext.cacheDir
}
return convertToImmutableConfig(configuration, buildUuid, packageInfo, appInfo)
return convertToImmutableConfig(
configuration,
buildUuid,
packageInfo,
appInfo,
lazy { configuration.persistenceDirectory ?: appContext.cacheDir }
)
}
internal const val RELEASE_STAGE_DEVELOPMENT = "development"

View File

@ -0,0 +1,18 @@
package com.bugsnag.android.internal.dag
import com.bugsnag.android.Configuration
import com.bugsnag.android.Connectivity
import com.bugsnag.android.internal.sanitiseConfiguration
/**
* A dependency module which constructs the configuration object that is used to alter
* Bugsnag's default behaviour.
*/
internal class ConfigModule(
contextModule: ContextModule,
configuration: Configuration,
connectivity: Connectivity
) : DependencyModule() {
val config = sanitiseConfiguration(contextModule.ctx, configuration, connectivity)
}

View File

@ -0,0 +1,17 @@
package com.bugsnag.android.internal.dag
import android.content.Context
/**
* A dependency module which accesses the application context object, falling back to the supplied
* context if it is the base context.
*/
internal class ContextModule(
appContext: Context
) : DependencyModule() {
val ctx: Context = when (appContext.applicationContext) {
null -> appContext
else -> appContext.applicationContext
}
}

View File

@ -0,0 +1,37 @@
package com.bugsnag.android.internal.dag
import com.bugsnag.android.BackgroundTaskService
import com.bugsnag.android.TaskType
internal abstract class DependencyModule {
private val properties = mutableListOf<Lazy<*>>()
/**
* Creates a new [Lazy] property that is marked as an object that should be resolved off the
* main thread when [resolveDependencies] is called.
*/
fun <T> future(initializer: () -> T): Lazy<T> {
val lazy = lazy {
initializer()
}
properties.add(lazy)
return lazy
}
/**
* Blocks until all dependencies in the module have been constructed. This provides the option
* for modules to construct objects in a background thread, then have a user block on another
* thread until all the objects have been constructed.
*/
fun resolveDependencies(bgTaskService: BackgroundTaskService, taskType: TaskType) {
kotlin.runCatching {
bgTaskService.submitTask(
taskType,
Runnable {
properties.forEach { it.value }
}
).get()
}
}
}

View File

@ -0,0 +1,15 @@
package com.bugsnag.android.internal.dag
import com.bugsnag.android.getActivityManager
import com.bugsnag.android.getStorageManager
/**
* A dependency module which provides a reference to Android system services.
*/
internal class SystemServiceModule(
contextModule: ContextModule
) : DependencyModule() {
val storageManager = contextModule.ctx.getStorageManager()
val activityManager = contextModule.ctx.getActivityManager()
}