mirror of https://github.com/M66B/FairEmail.git
Updated Bugsnag to version 5.11.0
This commit is contained in:
parent
ab1ae4220a
commit
a901aee4b7
|
@ -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"
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) }
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
)
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
}
|
Loading…
Reference in New Issue