/bugsnag-android-core/src/main/java/com/bugsnag/android/InternalReportDelegate.java

https://github.com/bugsnag/bugsnag-android · Java · 136 lines · 107 code · 21 blank · 8 comment · 4 complexity · 97690b55a87b16be2ec269bbbe9c5cf4 MD5 · raw file

  1. package com.bugsnag.android;
  2. import static com.bugsnag.android.DeliveryHeadersKt.HEADER_INTERNAL_ERROR;
  3. import static com.bugsnag.android.SeverityReason.REASON_UNHANDLED_EXCEPTION;
  4. import com.bugsnag.android.internal.ImmutableConfig;
  5. import android.annotation.SuppressLint;
  6. import android.content.Context;
  7. import android.os.Build;
  8. import android.os.storage.StorageManager;
  9. import androidx.annotation.NonNull;
  10. import androidx.annotation.Nullable;
  11. import java.io.File;
  12. import java.io.IOException;
  13. import java.util.Date;
  14. import java.util.Map;
  15. import java.util.concurrent.RejectedExecutionException;
  16. class InternalReportDelegate implements EventStore.Delegate {
  17. static final String INTERNAL_DIAGNOSTICS_TAB = "BugsnagDiagnostics";
  18. final Logger logger;
  19. final ImmutableConfig config;
  20. @Nullable
  21. final StorageManager storageManager;
  22. final AppDataCollector appDataCollector;
  23. final DeviceDataCollector deviceDataCollector;
  24. final Context appContext;
  25. final SessionTracker sessionTracker;
  26. final Notifier notifier;
  27. final BackgroundTaskService backgroundTaskService;
  28. InternalReportDelegate(Context context,
  29. Logger logger,
  30. ImmutableConfig immutableConfig,
  31. @Nullable StorageManager storageManager,
  32. AppDataCollector appDataCollector,
  33. DeviceDataCollector deviceDataCollector,
  34. SessionTracker sessionTracker,
  35. Notifier notifier,
  36. BackgroundTaskService backgroundTaskService) {
  37. this.logger = logger;
  38. this.config = immutableConfig;
  39. this.storageManager = storageManager;
  40. this.appDataCollector = appDataCollector;
  41. this.deviceDataCollector = deviceDataCollector;
  42. this.appContext = context;
  43. this.sessionTracker = sessionTracker;
  44. this.notifier = notifier;
  45. this.backgroundTaskService = backgroundTaskService;
  46. }
  47. @Override
  48. public void onErrorIOFailure(Exception exc, File errorFile, String context) {
  49. // send an internal error to bugsnag with no cache
  50. SeverityReason severityReason = SeverityReason.newInstance(REASON_UNHANDLED_EXCEPTION);
  51. Event err = new Event(exc, config, severityReason, logger);
  52. err.setContext(context);
  53. err.addMetadata(INTERNAL_DIAGNOSTICS_TAB, "canRead", errorFile.canRead());
  54. err.addMetadata(INTERNAL_DIAGNOSTICS_TAB, "canWrite", errorFile.canWrite());
  55. err.addMetadata(INTERNAL_DIAGNOSTICS_TAB, "exists", errorFile.exists());
  56. @SuppressLint("UsableSpace") // storagemanager alternative API requires API 26
  57. long usableSpace = appContext.getCacheDir().getUsableSpace();
  58. err.addMetadata(INTERNAL_DIAGNOSTICS_TAB, "usableSpace", usableSpace);
  59. err.addMetadata(INTERNAL_DIAGNOSTICS_TAB, "filename", errorFile.getName());
  60. err.addMetadata(INTERNAL_DIAGNOSTICS_TAB, "fileLength", errorFile.length());
  61. recordStorageCacheBehavior(err);
  62. reportInternalBugsnagError(err);
  63. }
  64. void recordStorageCacheBehavior(Event event) {
  65. if (storageManager != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
  66. File cacheDir = appContext.getCacheDir();
  67. File errDir = new File(cacheDir, "bugsnag-errors");
  68. try {
  69. boolean tombstone = storageManager.isCacheBehaviorTombstone(errDir);
  70. boolean group = storageManager.isCacheBehaviorGroup(errDir);
  71. event.addMetadata(INTERNAL_DIAGNOSTICS_TAB, "cacheTombstone", tombstone);
  72. event.addMetadata(INTERNAL_DIAGNOSTICS_TAB, "cacheGroup", group);
  73. } catch (IOException exc) {
  74. logger.w("Failed to record cache behaviour, skipping diagnostics", exc);
  75. }
  76. }
  77. }
  78. /**
  79. * Reports an event that occurred within the notifier to bugsnag. A lean event report will be
  80. * generated and sent asynchronously with no callbacks, retry attempts, or writing to disk.
  81. * This is intended for internal use only, and reports will not be visible to end-users.
  82. */
  83. void reportInternalBugsnagError(@NonNull Event event) {
  84. event.setApp(appDataCollector.generateAppWithState());
  85. event.setDevice(deviceDataCollector.generateDeviceWithState(new Date().getTime()));
  86. event.addMetadata(INTERNAL_DIAGNOSTICS_TAB, "notifierName", notifier.getName());
  87. event.addMetadata(INTERNAL_DIAGNOSTICS_TAB, "notifierVersion", notifier.getVersion());
  88. event.addMetadata(INTERNAL_DIAGNOSTICS_TAB, "apiKey", config.getApiKey());
  89. final EventPayload payload = new EventPayload(null, event, notifier, config);
  90. try {
  91. backgroundTaskService.submitTask(TaskType.INTERNAL_REPORT, new Runnable() {
  92. @Override
  93. public void run() {
  94. try {
  95. logger.d("InternalReportDelegate - sending internal event");
  96. Delivery delivery = config.getDelivery();
  97. DeliveryParams params = config.getErrorApiDeliveryParams(payload);
  98. // can only modify headers if DefaultDelivery is in use
  99. if (delivery instanceof DefaultDelivery) {
  100. Map<String, String> headers = params.getHeaders();
  101. headers.put(HEADER_INTERNAL_ERROR, "bugsnag-android");
  102. headers.remove(DeliveryHeadersKt.HEADER_API_KEY);
  103. DefaultDelivery defaultDelivery = (DefaultDelivery) delivery;
  104. defaultDelivery.deliver(params.getEndpoint(), payload, headers);
  105. }
  106. } catch (Exception exception) {
  107. logger.w("Failed to report internal event to Bugsnag", exception);
  108. }
  109. }
  110. });
  111. } catch (RejectedExecutionException ignored) {
  112. // drop internal report
  113. }
  114. }
  115. }