diff --git a/sentry-android-core/build.gradle.kts b/sentry-android-core/build.gradle.kts index c261c56f78..2c175f4c38 100644 --- a/sentry-android-core/build.gradle.kts +++ b/sentry-android-core/build.gradle.kts @@ -74,10 +74,19 @@ android { } } -tasks.withType().configureEach { - options.errorprone { - check("NullAway", net.ltgt.gradle.errorprone.CheckSeverity.ERROR) - option("NullAway:AnnotatedPackages", "io.sentry") +// net.ltgt.errorprone only configures Gradle's JVM source sets, not Android variants, so put Error +// Prone on each variant's processor path and enable it ourselves. +androidComponents.onVariants { variant -> + variant.annotationProcessorConfiguration.extendsFrom(configurations.errorprone.get()) + variant.configureJavaCompileTask { task -> + // Tells Error Prone this is Android code, so it skips checks that don't apply there, such as + // DefaultCharset (Android's default charset is always UTF-8), and runs its Android checks. + task.options.compilerArgs.add("-XDandroidCompatible=true") + task.options.errorprone { + enabled = true + check("NullAway", net.ltgt.gradle.errorprone.CheckSeverity.ERROR) + option("NullAway:AnnotatedPackages", "io.sentry") + } } } diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/internal/util/AndroidConnectionStatusProvider.java b/sentry-android-core/src/main/java/io/sentry/android/core/internal/util/AndroidConnectionStatusProvider.java index 256cf88e7b..df8a3b976d 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/internal/util/AndroidConnectionStatusProvider.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/internal/util/AndroidConnectionStatusProvider.java @@ -324,7 +324,7 @@ private void ensureNetworkCallbackRegistered() { public void onAvailable(final @NotNull Network network) { @NotNull ConnectionSnapshot current; do { - current = snapshot.get(); + current = currentSnapshot(); } while (!snapshot.compareAndSet(current, current.withNetwork(network))); // Only forward the call on first registration + when the connection is @@ -348,7 +348,7 @@ public void onUnavailable() { @Override public void onLost(final @NotNull Network network) { - if (!network.equals(snapshot.get().network)) { + if (!network.equals(currentSnapshot().network)) { return; } clearCacheAndNotifyObservers(); @@ -376,7 +376,7 @@ private void clearCacheAndNotifyObservers() { @Override public void onCapabilitiesChanged( @NonNull Network network, @NonNull NetworkCapabilities networkCapabilities) { - if (!network.equals(snapshot.get().network)) { + if (!network.equals(currentSnapshot().network)) { return; } updateCacheAndNotifyObservers(networkCapabilities); @@ -397,7 +397,7 @@ private void updateCacheAndNotifyObservers( @NotNull ConnectionSnapshot current; @NotNull ConnectionSnapshot updated; do { - current = snapshot.get(); + current = currentSnapshot(); if (!isSignificantChange(current.capabilities, networkCapabilities)) { return; } @@ -482,9 +482,15 @@ private boolean hasSignificantTransportChanges( } } + // NullAway treats AtomicReference.get() as nullable, but the snapshot is never set to null. + @SuppressWarnings("NullAway") + private @NotNull ConnectionSnapshot currentSnapshot() { + return snapshot.get(); + } + /** The current snapshot, refreshed first if it's stale. */ private @NotNull ConnectionSnapshot freshSnapshot() { - final @NotNull ConnectionSnapshot current = snapshot.get(); + final @NotNull ConnectionSnapshot current = currentSnapshot(); return current.isFresh() ? current : refresh(current); } @@ -510,7 +516,7 @@ private boolean hasSignificantTransportChanges( if (snapshot.compareAndSet(current, refreshed)) { break; } - current = snapshot.get(); + current = currentSnapshot(); if (current.isFresh()) { return current; } @@ -651,7 +657,7 @@ public void onForeground() { // proactively update cache and notify observers on foreground to ensure connectivity // state is not stale - final @NotNull ConnectionStatus status = statusOf(refresh(snapshot.get())); + final @NotNull ConnectionStatus status = statusOf(refresh(currentSnapshot())); if (status == ConnectionStatus.DISCONNECTED) { // onLost is not called retroactively when we registerNetworkCallback (as opposed to // onAvailable), so we have to do it manually for the DISCONNECTED case @@ -699,7 +705,7 @@ public void onBackground() { @TestOnly @Nullable public NetworkCapabilities getCachedNetworkCapabilities() { - return snapshot.get().capabilities; + return currentSnapshot().capabilities; } /** diff --git a/sentry-android-integration-tests/sentry-uitest-android/build.gradle.kts b/sentry-android-integration-tests/sentry-uitest-android/build.gradle.kts index 784bd463c5..557a5fab63 100644 --- a/sentry-android-integration-tests/sentry-uitest-android/build.gradle.kts +++ b/sentry-android-integration-tests/sentry-uitest-android/build.gradle.kts @@ -135,11 +135,22 @@ dependencies { androidTestImplementation(libs.okhttp.mockwebserver4) } -tasks.withType().configureEach { - options.errorprone { - check("NullAway", net.ltgt.gradle.errorprone.CheckSeverity.ERROR) - option("NullAway:AnnotatedPackages", "io.sentry") - option("NullAway:UnannotatedSubPackages", "io.sentry.uitest.android.databinding") +// net.ltgt.errorprone only configures Gradle's JVM source sets, not Android variants, so put Error +// Prone on each variant's processor path and enable it ourselves. +androidComponents.onVariants { variant -> + variant.annotationProcessorConfiguration.extendsFrom(configurations.errorprone.get()) + variant.configureJavaCompileTask { task -> + // Tells Error Prone this is Android code, so it skips checks that don't apply there, such as + // DefaultCharset (Android's default charset is always UTF-8), and runs its Android checks. + task.options.compilerArgs.add("-XDandroidCompatible=true") + task.options.errorprone { + enabled = true + check("NullAway", net.ltgt.gradle.errorprone.CheckSeverity.ERROR) + option("NullAway:AnnotatedPackages", "io.sentry") + option("NullAway:UnannotatedSubPackages", "io.sentry.uitest.android.databinding") + // AGP generates the view binding classes, so their findings can't be fixed here. + excludedPaths.set(".*/build/generated/.*") + } } }