diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/ActivityLifecycleIntegration.java b/sentry-android-core/src/main/java/io/sentry/android/core/ActivityLifecycleIntegration.java index 505bb84ea2c..09907849b34 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/ActivityLifecycleIntegration.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/ActivityLifecycleIntegration.java @@ -186,7 +186,7 @@ private void stopPreviousTransactions() { private void startTracing(final @NotNull Activity activity) { WeakReference weakActivity = new WeakReference<>(activity); - if (scopes != null && !isRunningTransactionOrTrace(activity)) { + if (scopes != null && options != null && !isRunningTransactionOrTrace(activity)) { if (!performanceEnabled) { activitiesWithOngoingTransactions.put(activity, NoOpTransaction.getInstance()); if (options.isEnableAutoTraceIdGeneration()) { @@ -309,7 +309,7 @@ private void startTracing(final @NotNull Activity activity) { // Continue either the foreground app.start above or an earlier headless app.start. final @Nullable String continueSentryTrace; final @Nullable String continueBaggage; - if (createStandaloneAppStart) { + if (createStandaloneAppStart && appStartTransaction != null) { continueSentryTrace = appStartTransaction.toSentryTrace().getValue(); final @Nullable BaggageHeader baggageHeader = appStartTransaction.toBaggageHeader(null); continueBaggage = baggageHeader == null ? null : baggageHeader.getValue(); @@ -359,7 +359,7 @@ private void startTracing(final @NotNull Activity activity) { final SpanOptions spanOptions = new SpanOptions(); setSpanOrigin(spanOptions); - if (isAppStart) { + if (isAppStart && coldStart != null) { if (!createStandaloneAppStart && !options.isEnableStandaloneAppStartTracing()) { appStartSpan = transaction.startChild( @@ -977,13 +977,16 @@ private void finishAppStartSpan() { } private void finishAppStartSpan(final @Nullable SentryDate endDate) { + if (!performanceEnabled || options == null) { + return; + } final @Nullable SentryDate appStartEndTime = endDate != null ? endDate : AppStartMetrics.getInstance() .getAppStartTimeSpanWithFallback(options) .getProjectedStopTimestamp(); - if (performanceEnabled && appStartEndTime != null) { + if (appStartEndTime != null) { finishSpan(appStartSpan, appStartEndTime); if (appStartTransaction != null && !appStartTransaction.isFinished()) { appStartTransaction.finish(SpanStatus.OK, appStartEndTime); @@ -1032,7 +1035,7 @@ private void onHeadlessAppStart() { } final @NotNull ITransaction transaction = - createStandaloneAppStartTransaction(startTime, null, false); + createStandaloneAppStartTransaction(scopes, options, startTime, null, false); transaction.finish(SpanStatus.OK, endTime); } @@ -1043,6 +1046,8 @@ private void onHeadlessAppStart() { * for its children and gets a deadline so it stays open until the extended span finishes. */ private @NotNull ITransaction createStandaloneAppStartTransaction( + final @NotNull IScopes scopes, + final @NotNull SentryAndroidOptions options, final @NotNull SentryDate startTime, final @Nullable TracesSamplingDecision samplingDecision, final boolean holdOpenForExtension) { @@ -1121,7 +1126,7 @@ private void onHeadlessAppStart() { metrics.setAppStartSamplingDecision(null); final @NotNull ITransaction transaction = - createStandaloneAppStartTransaction(startTime, samplingDecision, true); + createStandaloneAppStartTransaction(scopes, options, startTime, samplingDecision, true); final SpanOptions spanOptions = new SpanOptions(); setSpanOrigin(spanOptions); diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/AppState.java b/sentry-android-core/src/main/java/io/sentry/android/core/AppState.java index 74522f7aaac..24562e3aa49 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/AppState.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/AppState.java @@ -25,7 +25,7 @@ public final class AppState implements Closeable { private static @NotNull AppState instance = new AppState(); private final @NotNull AutoClosableReentrantLock lock = new AutoClosableReentrantLock(); - private volatile LifecycleObserver lifecycleObserver; + private volatile @Nullable LifecycleObserver lifecycleObserver; private MainLooperHandler handler = new MainLooperHandler(); private AppState() {} @@ -49,7 +49,7 @@ public void resetInstance() { @ApiStatus.Internal @TestOnly - public LifecycleObserver getLifecycleObserver() { + public @Nullable LifecycleObserver getLifecycleObserver() { return lifecycleObserver; } diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/DefaultAndroidEventProcessor.java b/sentry-android-core/src/main/java/io/sentry/android/core/DefaultAndroidEventProcessor.java index c7ea0e27e4f..75776dee6ae 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/DefaultAndroidEventProcessor.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/DefaultAndroidEventProcessor.java @@ -18,7 +18,6 @@ import io.sentry.protocol.SentryTransaction; import io.sentry.protocol.User; import io.sentry.util.HintUtils; -import io.sentry.util.LazyEvaluator; import io.sentry.util.Objects; import java.util.Collections; import java.util.List; @@ -40,8 +39,7 @@ final class DefaultAndroidEventProcessor implements SentryEventProcessor { private final @NotNull BuildInfoProvider buildInfoProvider; private final @NotNull SentryAndroidOptions options; @TestOnly final @Nullable Future deviceInfoUtil; - private final @NotNull LazyEvaluator deviceFamily = - new LazyEvaluator<>(() -> ContextUtils.getFamily(NoOpLogger.getInstance())); + private final @Nullable String deviceFamily = ContextUtils.getFamily(NoOpLogger.getInstance()); public DefaultAndroidEventProcessor( final @NotNull Context context, @@ -241,7 +239,7 @@ private void setDevice(final @NotNull SentryLogEvent event) { new SentryLogEventAttributeValue(SentryAttributeType.STRING, Build.MODEL)); event.setAttribute( "device.family", - new SentryLogEventAttributeValue(SentryAttributeType.STRING, deviceFamily.getValue())); + new SentryLogEventAttributeValue(SentryAttributeType.STRING, deviceFamily)); } catch (Throwable e) { options.getLogger().log(SentryLevel.ERROR, "Failed to retrieve device info", e); } @@ -269,7 +267,7 @@ private void setDevice(final @NotNull SentryMetricsEvent event) { new SentryLogEventAttributeValue(SentryAttributeType.STRING, Build.MODEL)); event.setAttribute( "device.family", - new SentryLogEventAttributeValue(SentryAttributeType.STRING, deviceFamily.getValue())); + new SentryLogEventAttributeValue(SentryAttributeType.STRING, deviceFamily)); } catch (Throwable e) { options.getLogger().log(SentryLevel.ERROR, "Failed to retrieve device info", e); } diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/FeedbackShakeIntegration.java b/sentry-android-core/src/main/java/io/sentry/android/core/FeedbackShakeIntegration.java index 4405cd19309..d8bf33b23da 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/FeedbackShakeIntegration.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/FeedbackShakeIntegration.java @@ -274,6 +274,7 @@ private void startShakeDetection(final @NotNull Activity activity) { final @Nullable WeakReference ref = currentActivityRef; final Activity active = ref != null ? ref.get() : null; final Boolean inBackground = AppState.getInstance().isInBackground(); + final @Nullable SentryAndroidOptions options = this.options; if (active == null || options == null || !enabled diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/SystemEventsBreadcrumbsIntegration.java b/sentry-android-core/src/main/java/io/sentry/android/core/SystemEventsBreadcrumbsIntegration.java index 3f45a9a9fb8..c203fca8de9 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/SystemEventsBreadcrumbsIntegration.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/SystemEventsBreadcrumbsIntegration.java @@ -66,8 +66,7 @@ public final class SystemEventsBreadcrumbsIntegration private final @NotNull String[] actions; private volatile boolean isClosed = false; private volatile boolean isStopped = false; - private volatile IntentFilter filter = null; - private volatile HandlerThread handlerThread = null; + private volatile @Nullable HandlerThread handlerThread = null; private final @NotNull AtomicBoolean isReceiverRegistered = new AtomicBoolean(false); private final @NotNull AutoClosableReentrantLock receiverLock = new AutoClosableReentrantLock(); // Track previous battery state to avoid duplicate breadcrumbs when values haven't changed @@ -146,17 +145,9 @@ private void registerReceiver( } receiver = new SystemEventsBroadcastReceiver(scopes, options); - if (filter == null) { - filter = new IntentFilter(); - for (String item : actions) { - filter.addAction(item); - } - } - if (customHandler == null && handlerThread == null) { - handlerThread = - new HandlerThread( - "SystemEventsReceiver", Process.THREAD_PRIORITY_BACKGROUND); - handlerThread.start(); + final @NotNull IntentFilter filter = new IntentFilter(); + for (String item : actions) { + filter.addAction(item); } try { // registerReceiver can throw SecurityException but it's not documented in the @@ -167,6 +158,12 @@ private void registerReceiver( if (customHandler != null) { handler = customHandler; } else { + if (handlerThread == null) { + handlerThread = + new HandlerThread( + "SystemEventsReceiver", Process.THREAD_PRIORITY_BACKGROUND); + handlerThread.start(); + } handler = new Handler(handlerThread.getLooper()); } ContextUtils.registerReceiver(context, options, receiver, filter, handler); @@ -198,6 +195,7 @@ private void registerReceiver( @SuppressWarnings("Convert2MethodRef") // older AGP versions do not support method references private void scheduleUnregisterReceiver() { + final @Nullable SentryAndroidOptions options = this.options; if (options == null) { return; } @@ -233,7 +231,6 @@ private void unregisterReceiver(final @NotNull SentryAndroidOptions options) { public void close() throws IOException { try (final @NotNull ISentryLifecycleToken ignored = receiverLock.acquire()) { isClosed = true; - filter = null; if (handlerThread != null) { handlerThread.quit(); } diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/ViewHierarchyEventProcessor.java b/sentry-android-core/src/main/java/io/sentry/android/core/ViewHierarchyEventProcessor.java index ffd3323e601..fb56d1778fb 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/ViewHierarchyEventProcessor.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/ViewHierarchyEventProcessor.java @@ -125,7 +125,7 @@ public ViewHierarchyEventProcessor(final @NotNull SentryAndroidOptions options) return event; } - public static byte[] snapshotViewHierarchyAsData( + public static byte @Nullable [] snapshotViewHierarchyAsData( @Nullable Activity activity, @NotNull IThreadChecker threadChecker, @NotNull ISerializer serializer, diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/anr/AnrProfileManager.java b/sentry-android-core/src/main/java/io/sentry/android/core/anr/AnrProfileManager.java index 62fce10c84d..dd3cffc3ffc 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/anr/AnrProfileManager.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/anr/AnrProfileManager.java @@ -61,7 +61,7 @@ public AnrProfileManager(final @NotNull SentryOptions options, final @NotNull Fi queueFile, new ObjectQueue.Converter() { @Override - public AnrStackTrace from(final byte[] source) throws IOException { + public @Nullable AnrStackTrace from(final byte[] source) throws IOException { // no need to close the streams since they are backed by byte arrays and don't // hold any resources final @NotNull ByteArrayInputStream bis = new ByteArrayInputStream(source); diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/internal/gestures/AndroidViewGestureTargetLocator.java b/sentry-android-core/src/main/java/io/sentry/android/core/internal/gestures/AndroidViewGestureTargetLocator.java index 5f6187cd39a..5799f262ecb 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/internal/gestures/AndroidViewGestureTargetLocator.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/internal/gestures/AndroidViewGestureTargetLocator.java @@ -40,7 +40,7 @@ && isViewScrollable(view, isAndroidXAvailable.getValue())) { return null; } - private UiElement createUiElement(final @NotNull View targetView) { + private @Nullable UiElement createUiElement(final @NotNull View targetView) { final @Nullable String resourceName = ViewUtils.getResourceIdOrNull(targetView); if (resourceName == null) { return null; diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/internal/threaddump/ThreadDumpParser.java b/sentry-android-core/src/main/java/io/sentry/android/core/internal/threaddump/ThreadDumpParser.java index e6c9430fa21..d64d5518804 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/internal/threaddump/ThreadDumpParser.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/internal/threaddump/ThreadDumpParser.java @@ -178,7 +178,7 @@ public void parse(final @NotNull Lines lines) { markThreads(); } - private SentryThread parseThread(final @NotNull Lines lines) { + private @Nullable SentryThread parseThread(final @NotNull Lines lines) { final SentryThread sentryThread = new SentryThread(); final Matcher beginManagedThreadRe = BEGIN_MANAGED_THREAD_RE.matcher(""); diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/internal/tombstone/TombstoneParser.java b/sentry-android-core/src/main/java/io/sentry/android/core/internal/tombstone/TombstoneParser.java index 4444e4981e7..244d3b4e74a 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/internal/tombstone/TombstoneParser.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/internal/tombstone/TombstoneParser.java @@ -316,6 +316,7 @@ void extendTo(final @NotNull MemoryMapping mapping) { this.previousOffset = mapping.offset; } + @Nullable DebugImage toDebugImage() { if (buildId.isEmpty()) { return null; diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/AndroidLoggerBatchProcessorTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/AndroidLoggerBatchProcessorTest.kt index 18bf67cd11c..8735d5bcebd 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/AndroidLoggerBatchProcessorTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/AndroidLoggerBatchProcessorTest.kt @@ -96,7 +96,7 @@ class AndroidLoggerBatchProcessorTest { val sut = fixture.getSut() sut.close(false) - assertTrue(AppState.getInstance().lifecycleObserver.listeners.isEmpty()) + assertTrue(AppState.getInstance().lifecycleObserver!!.listeners.isEmpty()) } @Test @@ -104,6 +104,6 @@ class AndroidLoggerBatchProcessorTest { val sut = fixture.getSut() sut.close(true) - assertTrue(AppState.getInstance().lifecycleObserver.listeners.isEmpty()) + assertTrue(AppState.getInstance().lifecycleObserver!!.listeners.isEmpty()) } } diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/AndroidMetricsBatchProcessorTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/AndroidMetricsBatchProcessorTest.kt index a965c066279..199533905e4 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/AndroidMetricsBatchProcessorTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/AndroidMetricsBatchProcessorTest.kt @@ -95,7 +95,7 @@ class AndroidMetricsBatchProcessorTest { val sut = fixture.getSut() sut.close(false) - assertTrue(AppState.getInstance().lifecycleObserver.listeners.isEmpty()) + assertTrue(AppState.getInstance().lifecycleObserver!!.listeners.isEmpty()) } @Test @@ -103,6 +103,6 @@ class AndroidMetricsBatchProcessorTest { val sut = fixture.getSut() sut.close(true) - assertTrue(AppState.getInstance().lifecycleObserver.listeners.isEmpty()) + assertTrue(AppState.getInstance().lifecycleObserver!!.listeners.isEmpty()) } } diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/SessionTrackingIntegrationTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/SessionTrackingIntegrationTest.kt index ef920e1d7fe..6c8eaa4d851 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/SessionTrackingIntegrationTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/SessionTrackingIntegrationTest.kt @@ -119,7 +119,7 @@ class SessionTrackingIntegrationTest { private fun setupLifecycle(): LifecycleRegistry { val lifecycle = LifecycleRegistry(ProcessLifecycleOwner.get()) - lifecycle.addObserver(AppState.getInstance().lifecycleObserver) + lifecycle.addObserver(AppState.getInstance().lifecycleObserver!!) return lifecycle } diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/SystemEventsBreadcrumbsIntegrationTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/SystemEventsBreadcrumbsIntegrationTest.kt index 2505b6f7f7d..60282aca26d 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/SystemEventsBreadcrumbsIntegrationTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/SystemEventsBreadcrumbsIntegrationTest.kt @@ -387,7 +387,7 @@ class SystemEventsBreadcrumbsIntegrationTest { sut.register(fixture.scopes, fixture.options) assertTrue( - AppState.getInstance().lifecycleObserver.listeners.any { + AppState.getInstance().lifecycleObserver!!.listeners.any { it is SystemEventsBreadcrumbsIntegration } ) @@ -401,7 +401,7 @@ class SystemEventsBreadcrumbsIntegrationTest { sut.register(fixture.scopes, fixture.options) assertFalse( - AppState.getInstance().lifecycleObserver.listeners.any { + AppState.getInstance().lifecycleObserver!!.listeners.any { it is SystemEventsBreadcrumbsIntegration } ) @@ -414,7 +414,7 @@ class SystemEventsBreadcrumbsIntegrationTest { sut.register(fixture.scopes, fixture.options) assertTrue( - AppState.getInstance().lifecycleObserver.listeners.any { + AppState.getInstance().lifecycleObserver!!.listeners.any { it is SystemEventsBreadcrumbsIntegration } ) @@ -422,7 +422,7 @@ class SystemEventsBreadcrumbsIntegrationTest { sut.close() assertFalse( - AppState.getInstance().lifecycleObserver.listeners.any { + AppState.getInstance().lifecycleObserver!!.listeners.any { it is SystemEventsBreadcrumbsIntegration } ) @@ -447,7 +447,7 @@ class SystemEventsBreadcrumbsIntegrationTest { shadowOf(Looper.getMainLooper()).idle() assertFalse( - AppState.getInstance().lifecycleObserver.listeners.any { + AppState.getInstance().lifecycleObserver!!.listeners.any { it is SystemEventsBreadcrumbsIntegration } ) diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/anr/AnrProfilingIntegrationTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/anr/AnrProfilingIntegrationTest.kt index 76cdac4d1e6..6c3d08c76bc 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/anr/AnrProfilingIntegrationTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/anr/AnrProfilingIntegrationTest.kt @@ -107,7 +107,7 @@ class AnrProfilingIntegrationTest { val thread = integration.getProperty("thread") assertNotNull(thread) - assertTrue(AppState.getInstance().lifecycleObserver.listeners.isNotEmpty()) + assertTrue(AppState.getInstance().lifecycleObserver!!.listeners.isNotEmpty()) integration.close() thread.join(2000) @@ -115,7 +115,7 @@ class AnrProfilingIntegrationTest { assertTrue(!thread.isAlive) val enabled = integration.getProperty("enabled") assertTrue(!enabled.get()) - assertTrue(AppState.getInstance().lifecycleObserver.listeners.isEmpty()) + assertTrue(AppState.getInstance().lifecycleObserver!!.listeners.isEmpty()) } @Test @@ -354,7 +354,7 @@ class AnrProfilingIntegrationTest { val integration = AnrProfilingIntegration() integration.register(mockScopes, androidOptions) - assertFalse(AppState.getInstance().lifecycleObserver.listeners.isEmpty()) + assertFalse(AppState.getInstance().lifecycleObserver!!.listeners.isEmpty()) assertTrue(SentryIntegrationPackageStorage.getInstance().integrations.contains("AnrProfiling")) integration.close() diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/internal/util/AndroidConnectionStatusProviderTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/internal/util/AndroidConnectionStatusProviderTest.kt index 56cf8f4e0ba..9c209006cb6 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/internal/util/AndroidConnectionStatusProviderTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/internal/util/AndroidConnectionStatusProviderTest.kt @@ -836,7 +836,7 @@ class AndroidConnectionStatusProviderTest { connectionStatusProvider.onBackground() assertFalse( - AppState.getInstance().lifecycleObserver.listeners.any { + AppState.getInstance().lifecycleObserver!!.listeners.any { it is SystemEventsBreadcrumbsIntegration } ) diff --git a/sentry/src/main/java/io/sentry/internal/gestures/UiElement.java b/sentry/src/main/java/io/sentry/internal/gestures/UiElement.java index c675910800a..92846cd8d96 100644 --- a/sentry/src/main/java/io/sentry/internal/gestures/UiElement.java +++ b/sentry/src/main/java/io/sentry/internal/gestures/UiElement.java @@ -51,7 +51,7 @@ public UiElement( } @Override - public boolean equals(Object o) { + public boolean equals(final @Nullable Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; UiElement uiElement = (UiElement) o; diff --git a/sentry/src/main/java/io/sentry/protocol/ViewHierarchyNode.java b/sentry/src/main/java/io/sentry/protocol/ViewHierarchyNode.java index 525d644fdc5..97fbea385c0 100644 --- a/sentry/src/main/java/io/sentry/protocol/ViewHierarchyNode.java +++ b/sentry/src/main/java/io/sentry/protocol/ViewHierarchyNode.java @@ -49,7 +49,7 @@ public void setRenderingSystem(String renderingSystem) { this.renderingSystem = renderingSystem; } - public void setType(String type) { + public void setType(final @Nullable String type) { this.type = type; }