diff --git a/CHANGELOG.md b/CHANGELOG.md index 1cfe1c307ba..9dbe9e60bee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -53,6 +53,7 @@ - Decide whether foregrounding the app starts a new session on a monotonic clock instead of the wall clock, so that a device time change no longer starts a session that should have been resumed, or resumes one that should have ended. This affects session counts ([#6096](https://github.com/getsentry/sentry-java/pull/6096)) - `SentryUserFeedbackForm` now extends `android.app.Dialog` instead of `android.app.AlertDialog` ([#6338](https://github.com/getsentry/sentry-java/pull/6338)) - The form never used `AlertDialog` features. Code that calls `AlertDialog`-only methods on it, such as `setButton` or `setMessage`, no longer compiles. +- Raise the minimum supported Android API level (`minSdk`) of all Android modules from 21 to 24. Apps with a lower `minSdk` must raise it or stay on 8.x ([#6518](https://github.com/getsentry/sentry-java/pull/6518)) - `sentry-android` no longer includes `sentry-android-ndk` as a default dependency. Instead, tombstone reporting is enabled by default, which provides more detailed native crashes on Android 11+ (API 30). If you're using sentry-android-ndk APIs, or need native crashes on Android 10 and lower, manually add the `io.sentry:sentry-android-ndk` dependency to your app. ([#6436](https://github.com/getsentry/sentry-java/pull/6436)) - Remove the Android `enablePerformanceV2` option and its `io.sentry.performance-v2.enable` manifest metadata. Performance V2 is always on ([#6458](https://github.com/getsentry/sentry-java/pull/6458)) - App starts are measured from process start on Android 7.0+ (API 24), and slow and frozen frames are measured per span diff --git a/README.md b/README.md index a073fcad6c1..89af6e7f128 100644 --- a/README.md +++ b/README.md @@ -18,17 +18,17 @@ Sentry SDK for Java and Android | Packages | Maven Central | Minimum Android API Version | |-----------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------| ------- | -| sentry-android | [![Maven Central Version](https://img.shields.io/maven-central/v/io.sentry/sentry-android?style=for-the-badge&logo=sentry&color=green)](https://central.sonatype.com/artifact/io.sentry/sentry-android) | 21 | -| sentry-android-core | [![Maven Central Version](https://img.shields.io/maven-central/v/io.sentry/sentry-android-core?style=for-the-badge&logo=sentry&color=green)](https://central.sonatype.com/artifact/io.sentry/sentry-android-core) | 21 | -| sentry-android-distribution | [![Maven Central Version](https://img.shields.io/maven-central/v/io.sentry/sentry-android-distribution?style=for-the-badge&logo=sentry&color=green)](https://central.sonatype.com/artifact/io.sentry/sentry-android-distribution) | 21 | -| sentry-android-ndk | [![Maven Central Version](https://img.shields.io/maven-central/v/io.sentry/sentry-android-ndk?style=for-the-badge&logo=sentry&color=green)](https://central.sonatype.com/artifact/io.sentry/sentry-android-ndk) | 21 | -| sentry-android-timber | [![Maven Central Version](https://img.shields.io/maven-central/v/io.sentry/sentry-android-timber?style=for-the-badge&logo=sentry&color=green)](https://central.sonatype.com/artifact/io.sentry/sentry-android-timber) | 21 | -| sentry-android-fragment | [![Maven Central Version](https://img.shields.io/maven-central/v/io.sentry/sentry-android-fragment?style=for-the-badge&logo=sentry&color=green)](https://central.sonatype.com/artifact/io.sentry/sentry-android-fragment) | 21 | -| sentry-android-navigation | [![Maven Central Version](https://img.shields.io/maven-central/v/io.sentry/sentry-android-navigation?style=for-the-badge&logo=sentry&color=green)](https://central.sonatype.com/artifact/io.sentry/sentry-android-navigation) | 21 | -| sentry-android-navigation3 | [![Maven Central Version](https://img.shields.io/maven-central/v/io.sentry/sentry-android-navigation3?style=for-the-badge&logo=sentry&color=green)](https://central.sonatype.com/artifact/io.sentry/sentry-android-navigation3) | 23 | -| sentry-android-sqlite | [![Maven Central Version](https://img.shields.io/maven-central/v/io.sentry/sentry-android-sqlite?style=for-the-badge&logo=sentry&color=green)](https://central.sonatype.com/artifact/io.sentry/sentry-android-sqlite) | 21 | +| sentry-android | [![Maven Central Version](https://img.shields.io/maven-central/v/io.sentry/sentry-android?style=for-the-badge&logo=sentry&color=green)](https://central.sonatype.com/artifact/io.sentry/sentry-android) | 24 | +| sentry-android-core | [![Maven Central Version](https://img.shields.io/maven-central/v/io.sentry/sentry-android-core?style=for-the-badge&logo=sentry&color=green)](https://central.sonatype.com/artifact/io.sentry/sentry-android-core) | 24 | +| sentry-android-distribution | [![Maven Central Version](https://img.shields.io/maven-central/v/io.sentry/sentry-android-distribution?style=for-the-badge&logo=sentry&color=green)](https://central.sonatype.com/artifact/io.sentry/sentry-android-distribution) | 24 | +| sentry-android-ndk | [![Maven Central Version](https://img.shields.io/maven-central/v/io.sentry/sentry-android-ndk?style=for-the-badge&logo=sentry&color=green)](https://central.sonatype.com/artifact/io.sentry/sentry-android-ndk) | 24 | +| sentry-android-timber | [![Maven Central Version](https://img.shields.io/maven-central/v/io.sentry/sentry-android-timber?style=for-the-badge&logo=sentry&color=green)](https://central.sonatype.com/artifact/io.sentry/sentry-android-timber) | 24 | +| sentry-android-fragment | [![Maven Central Version](https://img.shields.io/maven-central/v/io.sentry/sentry-android-fragment?style=for-the-badge&logo=sentry&color=green)](https://central.sonatype.com/artifact/io.sentry/sentry-android-fragment) | 24 | +| sentry-android-navigation | [![Maven Central Version](https://img.shields.io/maven-central/v/io.sentry/sentry-android-navigation?style=for-the-badge&logo=sentry&color=green)](https://central.sonatype.com/artifact/io.sentry/sentry-android-navigation) | 24 | +| sentry-android-navigation3 | [![Maven Central Version](https://img.shields.io/maven-central/v/io.sentry/sentry-android-navigation3?style=for-the-badge&logo=sentry&color=green)](https://central.sonatype.com/artifact/io.sentry/sentry-android-navigation3) | 24 | +| sentry-android-sqlite | [![Maven Central Version](https://img.shields.io/maven-central/v/io.sentry/sentry-android-sqlite?style=for-the-badge&logo=sentry&color=green)](https://central.sonatype.com/artifact/io.sentry/sentry-android-sqlite) | 24 | | sentry-android-replay | [![Maven Central Version](https://img.shields.io/maven-central/v/io.sentry/sentry-android-replay?style=for-the-badge&logo=sentry&color=green)](https://central.sonatype.com/artifact/io.sentry/sentry-android-replay) | 26 | -| sentry-compose-android | [![Maven Central Version](https://img.shields.io/maven-central/v/io.sentry/sentry-compose-android?style=for-the-badge&logo=sentry&color=green)](https://central.sonatype.com/artifact/io.sentry/sentry-compose-android) | 21 | +| sentry-compose-android | [![Maven Central Version](https://img.shields.io/maven-central/v/io.sentry/sentry-compose-android?style=for-the-badge&logo=sentry&color=green)](https://central.sonatype.com/artifact/io.sentry/sentry-compose-android) | 24 | | sentry-compose-desktop | [![Maven Central Version](https://img.shields.io/maven-central/v/io.sentry/sentry-compose-desktop?style=for-the-badge&logo=sentry&color=green)](https://central.sonatype.com/artifact/io.sentry/sentry-compose-desktop) | | sentry-compose | [![Maven Central Version](https://img.shields.io/maven-central/v/io.sentry/sentry-compose?style=for-the-badge&logo=sentry&color=green)](https://central.sonatype.com/artifact/io.sentry/sentry-compose) | | sentry | [![Maven Central Version](https://img.shields.io/maven-central/v/io.sentry/sentry?style=for-the-badge&logo=sentry&color=green)](https://central.sonatype.com/artifact/io.sentry/sentry) | 21 | diff --git a/build.gradle.kts b/build.gradle.kts index 6a42fe07a42..1f0e87fefcc 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -33,6 +33,8 @@ buildscript { google() } dependencies { + // R8 must come before AGP on the classpath to replace the R8 bundled with AGP. + Config.BuildPlugins.r8?.let { classpath(it) } classpath(Config.BuildPlugins.androidGradle) // add classpath of sentry android gradle plugin diff --git a/buildSrc/src/main/java/Config.kt b/buildSrc/src/main/java/Config.kt index 05786658ef3..78991110635 100644 --- a/buildSrc/src/main/java/Config.kt +++ b/buildSrc/src/main/java/Config.kt @@ -7,6 +7,11 @@ object Config { object BuildPlugins { val androidGradle = "com.android.tools.build:gradle:$AGP" + + // The R8 bundled with AGP 9.0 and 9.2 crashes on androidx.core at minSdk 24, so builds that + // override AGP (the AGP matrix) run the R8 that ships with the default AGP instead. + val r8: String? + get() = if (System.getenv("VERSION_AGP") != null) "com.android.tools:r8:9.4.14" else null } object Android { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 49f20a5b213..ec1280cc278 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -55,7 +55,7 @@ sqldelight = "2.3.2" targetSdk = "37" compileSdk = "37" compileSdkMinor = "2" -minSdk = "21" +minSdk = "24" [plugins] kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/AndroidContinuousProfiler.java b/sentry-android-core/src/main/java/io/sentry/android/core/AndroidContinuousProfiler.java index 4f115ca9d7f..23a72b131fc 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/AndroidContinuousProfiler.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/AndroidContinuousProfiler.java @@ -4,7 +4,6 @@ import static io.sentry.IConnectionStatusProvider.ConnectionStatus.DISCONNECTED; import static java.util.concurrent.TimeUnit.SECONDS; -import android.os.Build; import io.sentry.CompositePerformanceCollector; import io.sentry.DataCategory; import io.sentry.IContinuousProfiler; @@ -176,10 +175,6 @@ private void initScopes() { private void start() { initScopes(); - // Debug.startMethodTracingSampling() is only available since Lollipop, but Android Profiler - // causes crashes on api 21 -> https://github.com/getsentry/sentry-java/issues/3392 - if (buildInfoProvider.getSdkInfoVersion() < Build.VERSION_CODES.LOLLIPOP_MR1) return; - // Let's initialize trace folder and profiling interval init(); // init() didn't create profiler, should never happen @@ -281,12 +276,6 @@ private void stop(final boolean restartProfiler) { return; } - // onTransactionStart() is only available since Lollipop_MR1 - // and SystemClock.elapsedRealtimeNanos() since Jelly Bean - if (buildInfoProvider.getSdkInfoVersion() < Build.VERSION_CODES.LOLLIPOP_MR1) { - return; - } - List performanceCollectionData = null; if (performanceCollector != null) { performanceCollectionData = performanceCollector.stop(chunkId.toString()); diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/AndroidProfiler.java b/sentry-android-core/src/main/java/io/sentry/android/core/AndroidProfiler.java index 3f569df5378..8710c5d637f 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/AndroidProfiler.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/AndroidProfiler.java @@ -1,6 +1,5 @@ package io.sentry.android.core; -import android.annotation.SuppressLint; import android.os.Debug; import android.os.Process; import android.os.SystemClock; @@ -116,7 +115,6 @@ public AndroidProfiler( Objects.requireNonNull(frameMetricsCollector, "SentryFrameMetricsCollector is required"); } - @SuppressLint("NewApi") public @Nullable ProfileStartData start() { try (final @NotNull ISentryLifecycleToken ignored = lock.acquire()) { // intervalUs is 0 only if there was a problem in the init @@ -225,7 +223,6 @@ public void onFrameMetricCollected( } } - @SuppressLint("NewApi") public @Nullable ProfileEndData endAndCollect( final boolean isTimeout, final @Nullable List performanceCollectionData) { @@ -299,7 +296,6 @@ public void close() { } } - @SuppressLint("NewApi") private void putPerformanceCollectionDataInMeasurements( final @Nullable List performanceCollectionData) { diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/AndroidTransactionProfiler.java b/sentry-android-core/src/main/java/io/sentry/android/core/AndroidTransactionProfiler.java index e44ed746a08..f5d65ed4bed 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/AndroidTransactionProfiler.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/AndroidTransactionProfiler.java @@ -2,7 +2,6 @@ import static java.util.concurrent.TimeUnit.SECONDS; -import android.annotation.SuppressLint; import android.content.Context; import android.os.Build; import io.sentry.DateUtils; @@ -151,10 +150,6 @@ private void init() { @Override public void start() { - // Debug.startMethodTracingSampling() is only available since Lollipop, but Android Profiler - // causes crashes on api 21 -> https://github.com/getsentry/sentry-java/issues/3392 - if (buildInfoProvider.getSdkInfoVersion() < Build.VERSION_CODES.LOLLIPOP_MR1) return; - // When the first transaction is starting, we can start profiling if (!isRunning.getAndSet(true)) { // Let's initialize trace folder and profiling interval @@ -179,7 +174,6 @@ public void start() { } } - @SuppressLint("NewApi") private boolean onFirstStart() { // init() didn't create profiler, should never happen if (profiler == null) { @@ -225,7 +219,6 @@ public void bindTransaction(final @NotNull ITransaction transaction) { options); } - @SuppressLint("NewApi") private @Nullable ProfilingTraceData onTransactionFinish( final @NotNull String transactionName, final @NotNull String transactionId, @@ -234,11 +227,6 @@ public void bindTransaction(final @NotNull ITransaction transaction) { final @Nullable List performanceCollectionData, final @NotNull SentryOptions options) { - // onTransactionStart() is only available since Lollipop_MR1 - // and SystemClock.elapsedRealtimeNanos() since Jelly Bean - // and SUPPORTED_ABIS since KITKAT - if (buildInfoProvider.getSdkInfoVersion() < Build.VERSION_CODES.LOLLIPOP_MR1) return null; - // check if profiler was created if (profiler == null) { return null; diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/AnrV2Integration.java b/sentry-android-core/src/main/java/io/sentry/android/core/AnrV2Integration.java index 5e5500297f8..c80ce982b95 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/AnrV2Integration.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/AnrV2Integration.java @@ -62,7 +62,6 @@ public AnrV2Integration(final @NotNull Context context) { this.dateProvider = dateProvider; } - @SuppressLint("NewApi") // we do the check in the AnrIntegrationFactory @Override public void register(@NotNull IScopes scopes, @NotNull SentryOptions options) { this.options = diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/ApplicationExitInfoEventProcessor.java b/sentry-android-core/src/main/java/io/sentry/android/core/ApplicationExitInfoEventProcessor.java index 80da05967ea..79e671b4f49 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/ApplicationExitInfoEventProcessor.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/ApplicationExitInfoEventProcessor.java @@ -18,7 +18,6 @@ import static io.sentry.cache.PersistingScopeObserver.USER_FILENAME; import static io.sentry.protocol.Contexts.REPLAY_ID; -import android.annotation.SuppressLint; import android.app.ActivityManager; import android.content.Context; import android.content.pm.PackageInfo; @@ -728,7 +727,6 @@ private void setDevice(final @NotNull SentryBaseEvent event) { // only use static data that does not change between app launches (e.g. timezone, boottime, // battery level will change) - @SuppressLint("NewApi") private @NotNull Device getDevice() { Device device = new Device(); device.setManufacturer(Build.MANUFACTURER); diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/ContextUtils.java b/sentry-android-core/src/main/java/io/sentry/android/core/ContextUtils.java index 60ae00f2ead..b189746fde6 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/ContextUtils.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/ContextUtils.java @@ -218,7 +218,6 @@ static PackageInfo getPackageInfo( * * @return the Application's ApplicationInfo if possible, or throws */ - @SuppressLint("NewApi") @Nullable @SuppressWarnings("deprecation") static ApplicationInfo getApplicationInfo( diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/DeviceInfoUtil.java b/sentry-android-core/src/main/java/io/sentry/android/core/DeviceInfoUtil.java index 7d45ebcf6e5..3cc0b3c67f5 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/DeviceInfoUtil.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/DeviceInfoUtil.java @@ -247,7 +247,6 @@ private void setDeviceIO( } } - @SuppressWarnings("NewApi") @NotNull private TimeZone getTimeZone() { // Only use the costly Calendar API on Android 13+ (API Level 33+) when the locale contains a diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/NetworkBreadcrumbsIntegration.java b/sentry-android-core/src/main/java/io/sentry/android/core/NetworkBreadcrumbsIntegration.java index c7f3182b97d..0151c96de67 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/NetworkBreadcrumbsIntegration.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/NetworkBreadcrumbsIntegration.java @@ -64,12 +64,6 @@ public void register(final @NotNull IScopes scopes, final @NotNull SentryOptions if (androidOptions.isEnableNetworkEventBreadcrumbs()) { - // The specific error is logged in the ConnectivityChecker method - if (buildInfoProvider.getSdkInfoVersion() < Build.VERSION_CODES.N) { - options.getLogger().log(SentryLevel.DEBUG, "NetworkCallbacks need Android N+."); - return; - } - try (final @NotNull ISentryLifecycleToken ignored = lock.acquire()) { networkCallback = new NetworkBreadcrumbsNetworkCallback( diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroid.java b/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroid.java index e649e9e094c..1544a721c10 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroid.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroid.java @@ -1,6 +1,5 @@ package io.sentry.android.core; -import android.annotation.SuppressLint; import android.app.Application; import android.content.Context; import android.os.Process; @@ -91,7 +90,6 @@ public static void init( * @param logger your custom logger that implements ILogger * @param configuration Sentry.OptionsConfiguration configuration handler */ - @SuppressLint("NewApi") public static void init( @NotNull final Context context, @NotNull ILogger logger, @@ -154,11 +152,9 @@ public static void init( // if SentryPerformanceProvider was disabled or removed, // we set the app start / sdk init time here instead final @NotNull AppStartMetrics appStartMetrics = AppStartMetrics.getInstance(); - if (buildInfoProvider.getSdkInfoVersion() >= android.os.Build.VERSION_CODES.N) { - final @NotNull TimeSpan appStartTimeSpan = appStartMetrics.getAppStartTimeSpan(); - if (appStartTimeSpan.hasNotStarted()) { - appStartTimeSpan.setStartedAt(Process.getStartUptimeMillis()); - } + final @NotNull TimeSpan appStartTimeSpan = appStartMetrics.getAppStartTimeSpan(); + if (appStartTimeSpan.hasNotStarted()) { + appStartTimeSpan.setStartedAt(Process.getStartUptimeMillis()); } if (context.getApplicationContext() instanceof Application) { appStartMetrics.registerLifecycleCallbacks( diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/SentryPerformanceProvider.java b/sentry-android-core/src/main/java/io/sentry/android/core/SentryPerformanceProvider.java index 9d1f2e13ec4..d156b00fa5a 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/SentryPerformanceProvider.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/SentryPerformanceProvider.java @@ -2,7 +2,6 @@ import static io.sentry.Sentry.APP_START_PROFILING_CONFIG_FILE_NAME; -import android.annotation.SuppressLint; import android.app.Application; import android.content.Context; import android.content.pm.ProviderInfo; @@ -250,7 +249,6 @@ private void createAndStartTransactionProfiler( appStartProfiler.start(); } - @SuppressLint("NewApi") private void onAppLaunched( final @Nullable Context context, final @NotNull AppStartMetrics appStartMetrics) { @@ -258,12 +256,8 @@ private void onAppLaunched( final @NotNull TimeSpan sdkInitTimeSpan = appStartMetrics.getSdkInitTimeSpan(); sdkInitTimeSpan.setStartedAt(sdkInitMillis); - // performance v2: Uses Process.getStartUptimeMillis() - // requires API level 24+ - if (buildInfoProvider.getSdkInfoVersion() >= android.os.Build.VERSION_CODES.N) { - final @NotNull TimeSpan appStartTimespan = appStartMetrics.getAppStartTimeSpan(); - appStartTimespan.setStartedAt(Process.getStartUptimeMillis()); - } + final @NotNull TimeSpan appStartTimespan = appStartMetrics.getAppStartTimeSpan(); + appStartTimespan.setStartedAt(Process.getStartUptimeMillis()); if (context instanceof Application) { app = (Application) context; diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/internal/gestures/WindowCallbackAdapter.java b/sentry-android-core/src/main/java/io/sentry/android/core/internal/gestures/WindowCallbackAdapter.java index c6a3e627115..a44643934ab 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/internal/gestures/WindowCallbackAdapter.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/internal/gestures/WindowCallbackAdapter.java @@ -1,6 +1,5 @@ package io.sentry.android.core.internal.gestures; -import android.annotation.SuppressLint; import android.view.ActionMode; import android.view.KeyEvent; import android.view.Menu; @@ -115,7 +114,6 @@ public boolean onSearchRequested() { return delegate.onSearchRequested(); } - @SuppressLint("NewApi") @Override public boolean onSearchRequested(SearchEvent searchEvent) { return delegate.onSearchRequested(searchEvent); @@ -127,7 +125,6 @@ public ActionMode onWindowStartingActionMode(ActionMode.Callback callback) { return delegate.onWindowStartingActionMode(callback); } - @SuppressLint("NewApi") @Nullable @Override public ActionMode onWindowStartingActionMode(ActionMode.Callback callback, int i) { 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 62a212e6dcc..a5ff05e27d5 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 @@ -99,9 +99,7 @@ public AndroidConnectionStatusProvider( new CellularNetworkTechnologyProvider(this.context, options.getLogger(), buildInfoProvider); capabilities[0] = NetworkCapabilities.NET_CAPABILITY_INTERNET; - if (buildInfoProvider.getSdkInfoVersion() >= Build.VERSION_CODES.M) { - capabilities[1] = NetworkCapabilities.NET_CAPABILITY_VALIDATED; - } + capabilities[1] = NetworkCapabilities.NET_CAPABILITY_VALIDATED; // Register network callback immediately for caching //noinspection Convert2MethodRef @@ -123,13 +121,9 @@ private boolean isNetworkEffectivelyConnected( } // Check for general internet capability AND validated status - boolean hasInternetAndValidated = - networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); - if (buildInfoProvider.getSdkInfoVersion() >= Build.VERSION_CODES.M) { - hasInternetAndValidated = - hasInternetAndValidated - && networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED); - } + final boolean hasInternetAndValidated = + networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) + && networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED); if (!hasInternetAndValidated) { return false; @@ -375,7 +369,7 @@ private boolean hasSignificantTransportChanges( } } - @SuppressLint({"NewApi", "MissingPermission"}) + @SuppressLint("MissingPermission") private void updateCache(@Nullable NetworkCapabilities networkCapabilities) { try (final @NotNull ISentryLifecycleToken ignored = lock.acquire()) { try { @@ -393,12 +387,6 @@ private void updateCache(@Nullable NetworkCapabilities networkCapabilities) { return; } - if (buildInfoProvider.getSdkInfoVersion() < Build.VERSION_CODES.M) { - cachedNetworkCapabilities = null; - cacheFreshUntil = Deadline.after(ticker, CACHE_TTL_MINUTES, TimeUnit.MINUTES); - return; - } - // Fallback: query current active network final ConnectivityManager connectivityManager = getConnectivityManager(context, options.getLogger()); @@ -611,7 +599,7 @@ public NetworkCapabilities getCachedNetworkCapabilities() { * @param buildInfoProvider the BuildInfoProvider provider * @return the connection type wifi, ethernet, cellular or null */ - @SuppressLint({"ObsoleteSdkInt", "MissingPermission", "NewApi"}) + @SuppressLint("MissingPermission") public static @Nullable String getConnectionType( final @NotNull Context context, final @NotNull ILogger logger, @@ -630,65 +618,25 @@ public NetworkCapabilities getCachedNetworkCapabilities() { boolean wifi = false; boolean cellular = false; - if (buildInfoProvider.getSdkInfoVersion() >= Build.VERSION_CODES.M) { - - final Network activeNetwork = connectivityManager.getActiveNetwork(); - if (activeNetwork == null) { - logger.log(SentryLevel.INFO, "Network is null and cannot check network status"); - return null; - } - final NetworkCapabilities networkCapabilities = - connectivityManager.getNetworkCapabilities(activeNetwork); - if (networkCapabilities == null) { - logger.log(SentryLevel.INFO, "NetworkCapabilities is null and cannot check network type"); - return null; - } - if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)) { - ethernet = true; - } - if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) { - wifi = true; - } - if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) { - cellular = true; - } - } else { - // ideally using connectivityManager.getAllNetworks(), but its >= Android L only - - // for some reason linting didn't allow a single @SuppressWarnings("deprecation") on method - // signature - @SuppressWarnings("deprecation") - final android.net.NetworkInfo activeNetworkInfo = - connectivityManager.getActiveNetworkInfo(); - - if (activeNetworkInfo == null) { - logger.log(SentryLevel.INFO, "NetworkInfo is null, there's no active network."); - return null; - } - - @SuppressWarnings("deprecation") - final int type = activeNetworkInfo.getType(); - - @SuppressWarnings("deprecation") - final int TYPE_ETHERNET = ConnectivityManager.TYPE_ETHERNET; - - @SuppressWarnings("deprecation") - final int TYPE_WIFI = ConnectivityManager.TYPE_WIFI; - - @SuppressWarnings("deprecation") - final int TYPE_MOBILE = ConnectivityManager.TYPE_MOBILE; - - switch (type) { - case TYPE_ETHERNET: - ethernet = true; - break; - case TYPE_WIFI: - wifi = true; - break; - case TYPE_MOBILE: - cellular = true; - break; - } + final Network activeNetwork = connectivityManager.getActiveNetwork(); + if (activeNetwork == null) { + logger.log(SentryLevel.INFO, "Network is null and cannot check network status"); + return null; + } + final NetworkCapabilities networkCapabilities = + connectivityManager.getNetworkCapabilities(activeNetwork); + if (networkCapabilities == null) { + logger.log(SentryLevel.INFO, "NetworkCapabilities is null and cannot check network type"); + return null; + } + if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)) { + ethernet = true; + } + if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) { + wifi = true; + } + if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) { + cellular = true; } // TODO: change the protocol to be a list of transports as a device may have the capability of @@ -758,11 +706,6 @@ public static boolean addNetworkCallback( final @NotNull ILogger logger, final @NotNull BuildInfoProvider buildInfoProvider, final @NotNull NetworkCallback networkCallback) { - if (buildInfoProvider.getSdkInfoVersion() < Build.VERSION_CODES.N) { - logger.log(SentryLevel.DEBUG, "NetworkCallbacks need Android N+."); - return false; - } - if (!Permissions.hasPermission(context, Manifest.permission.ACCESS_NETWORK_STATE)) { logger.log(SentryLevel.INFO, "No permission (ACCESS_NETWORK_STATE) to check network status."); return false; @@ -787,10 +730,6 @@ static boolean registerNetworkCallback( final @NotNull BuildInfoProvider buildInfoProvider, final @Nullable Handler handler, final @NotNull NetworkCallback networkCallback) { - if (buildInfoProvider.getSdkInfoVersion() < Build.VERSION_CODES.N) { - logger.log(SentryLevel.DEBUG, "NetworkCallbacks need Android N+."); - return false; - } final ConnectivityManager connectivityManager = getConnectivityManager(context, logger); if (connectivityManager == null) { return false; @@ -812,7 +751,6 @@ static boolean registerNetworkCallback( return true; } - @SuppressLint("NewApi") static void unregisterNetworkCallback( final @NotNull Context context, final @NotNull ILogger logger, diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/internal/util/CellularNetworkTechnologyProvider.java b/sentry-android-core/src/main/java/io/sentry/android/core/internal/util/CellularNetworkTechnologyProvider.java index a5468ae913f..f26a57103ad 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/internal/util/CellularNetworkTechnologyProvider.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/internal/util/CellularNetworkTechnologyProvider.java @@ -142,12 +142,8 @@ public void unregister() { } } - @SuppressLint({"MissingPermission", "InlinedApi", "NewApi"}) + @SuppressLint({"MissingPermission", "InlinedApi"}) private @Nullable String getDataNetworkTechnology() { - if (buildInfoProvider.getSdkInfoVersion() < Build.VERSION_CODES.N) { - // getDataNetworkType was added in API 24. - return null; - } // The SDK doesn't declare these permissions, so the technology is only available to apps that // already request one of them. READ_BASIC_PHONE_STATE only exists from API 31 on, where the // display info listener is used instead, but checking it is harmless. diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/internal/util/ContentProviderSecurityChecker.java b/sentry-android-core/src/main/java/io/sentry/android/core/internal/util/ContentProviderSecurityChecker.java index 9aa21507f73..61cb813e53d 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/internal/util/ContentProviderSecurityChecker.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/internal/util/ContentProviderSecurityChecker.java @@ -1,6 +1,5 @@ package io.sentry.android.core.internal.util; -import android.annotation.SuppressLint; import android.content.ContentProvider; import android.os.Build; import io.sentry.NoOpLogger; @@ -42,7 +41,6 @@ public ContentProviderSecurityChecker(final @NotNull BuildInfoProvider buildInfo *

The vulnerability is specific to un-patched versions of Android 8 and 9 (API 26 to 28). * Therefore, this security check is limited to those versions to mitigate risk of regression. */ - @SuppressLint("NewApi") public void checkPrivilegeEscalation(@NotNull ContentProvider contentProvider) { final int sdkVersion = buildInfoProvider.getSdkInfoVersion(); if (sdkVersion >= Build.VERSION_CODES.O && sdkVersion <= Build.VERSION_CODES.P) { diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/internal/util/ScreenshotUtils.java b/sentry-android-core/src/main/java/io/sentry/android/core/internal/util/ScreenshotUtils.java index 00d9cc37e3c..76700beaf4a 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/internal/util/ScreenshotUtils.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/internal/util/ScreenshotUtils.java @@ -40,7 +40,6 @@ public class ScreenshotUtils { } // Used by Hybrid SDKs - @SuppressLint("NewApi") public static @Nullable byte[] takeScreenshot( final @NotNull Activity activity, final @NotNull IThreadChecker threadChecker, diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/internal/util/SentryFrameMetricsCollector.java b/sentry-android-core/src/main/java/io/sentry/android/core/internal/util/SentryFrameMetricsCollector.java index 2c0ae246558..1af8f97fea2 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/internal/util/SentryFrameMetricsCollector.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/internal/util/SentryFrameMetricsCollector.java @@ -12,7 +12,6 @@ import android.view.Choreographer; import android.view.FrameMetrics; import android.view.Window; -import androidx.annotation.RequiresApi; import io.sentry.ILogger; import io.sentry.ISentryLifecycleToken; import io.sentry.SentryLevel; @@ -65,7 +64,6 @@ public final class SentryFrameMetricsCollector implements Application.ActivityLi private final @NotNull ConcurrentSkipListSet delayedFrames = new ConcurrentSkipListSet<>(); - @SuppressLint("NewApi") public SentryFrameMetricsCollector( final @NotNull Context context, final @NotNull SentryOptions options, @@ -73,7 +71,6 @@ public SentryFrameMetricsCollector( this(context, options, buildInfoProvider, new WindowFrameMetricsManager() {}); } - @SuppressLint("NewApi") public SentryFrameMetricsCollector( final @NotNull Context context, final @NotNull ILogger logger, @@ -81,7 +78,7 @@ public SentryFrameMetricsCollector( this(context, logger, buildInfoProvider, new WindowFrameMetricsManager() {}); } - @SuppressLint({"NewApi", "DiscouragedPrivateApi"}) + @SuppressLint("DiscouragedPrivateApi") public SentryFrameMetricsCollector( final @NotNull Context context, final @NotNull SentryOptions options, @@ -110,10 +107,6 @@ public SentryFrameMetricsCollector( if (!(appContext instanceof Application)) { return; } - // FrameMetrics api is only available since sdk version N - if (buildInfoProvider.getSdkInfoVersion() < Build.VERSION_CODES.N) { - return; - } isAvailable = true; // The frame metrics HandlerThread is started lazily on the first startCollection() call. @@ -240,7 +233,6 @@ private long getFrameStartTimestamp(final @NotNull FrameMetrics frameMetrics) { * Return time spent on the main thread (cpu) for frame creation. It doesn't consider time spent * on the render thread (gpu). */ - @RequiresApi(api = Build.VERSION_CODES.N) private long getFrameCpuDuration(final @NotNull FrameMetrics frameMetrics) { // Inspired by JankStats // https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:metrics/metrics-performance/src/main/java/androidx/metrics/performance/JankStatsApi24Impl.kt;l=74-79;drc=1de6215c6bd9e887e3d94556e9ac55cfb7b8c797 @@ -327,7 +319,6 @@ public void stopCollection(final @Nullable String listenerId) { } } - @SuppressLint("NewApi") private void stopTrackingWindow(final @NotNull Window window) { new Handler(Looper.getMainLooper()) .post( @@ -353,7 +344,6 @@ private void setCurrentWindow(final @NotNull Window window) { trackCurrentWindow(); } - @SuppressLint("NewApi") private void trackCurrentWindow() { @Nullable Window window = currentWindow != null ? currentWindow.get() : null; if (window == null || !isAvailable) { @@ -512,7 +502,6 @@ void onFrameMetricCollected( @ApiStatus.Internal public interface WindowFrameMetricsManager { - @RequiresApi(api = Build.VERSION_CODES.N) default void addOnFrameMetricsAvailableListener( final @NotNull Window window, final @Nullable Window.OnFrameMetricsAvailableListener frameMetricsAvailableListener, @@ -523,7 +512,6 @@ default void addOnFrameMetricsAvailableListener( window.addOnFrameMetricsAvailableListener(frameMetricsAvailableListener, handler); } - @RequiresApi(api = Build.VERSION_CODES.N) default void removeOnFrameMetricsAvailableListener( final @NotNull Window window, final @Nullable Window.OnFrameMetricsAvailableListener frameMetricsAvailableListener) { diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/performance/AppStartMetrics.java b/sentry-android-core/src/main/java/io/sentry/android/core/performance/AppStartMetrics.java index a23b9a8720b..e49bada831a 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/performance/AppStartMetrics.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/performance/AppStartMetrics.java @@ -550,28 +550,15 @@ private void scheduleHeadlessAppStartCheckOnMain() { if (!headlessAppStartCheckPending.compareAndSet(false, true)) { return; } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - Looper.getMainLooper() - .getQueue() - .addIdleHandler( - () -> { - firstIdle = SystemClock.uptimeMillis(); - headlessAppStartCheckPending.set(false); - handleHeadlessAppStartIfNeededOnMain(); - return false; - }); - } else { - final Handler handler = new Handler(Looper.getMainLooper()); - handler.post( - () -> { - firstIdle = SystemClock.uptimeMillis(); - handler.post( - () -> { - headlessAppStartCheckPending.set(false); - handleHeadlessAppStartIfNeededOnMain(); - }); - }); - } + Looper.getMainLooper() + .getQueue() + .addIdleHandler( + () -> { + firstIdle = SystemClock.uptimeMillis(); + headlessAppStartCheckPending.set(false); + handleHeadlessAppStartIfNeededOnMain(); + return false; + }); } /** diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/ActivityLifecycleIntegrationTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/ActivityLifecycleIntegrationTest.kt index ae215d29efb..d2a8f05b996 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/ActivityLifecycleIntegrationTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/ActivityLifecycleIntegrationTest.kt @@ -691,33 +691,6 @@ class ActivityLifecycleIntegrationTest { assertEquals(SpanStatus.OK, transaction.status) } - @Test - @Config(sdk = [Build.VERSION_CODES.M]) - fun `onHeadlessAppStart creates standalone App Start transaction on API 23`() { - val sut = fixture.getSut { - it.tracesSampleRate = 1.0 - it.isEnableStandaloneAppStartTracing = true - } - sut.register(fixture.scopes, fixture.options) - prepareHeadlessSdkInitAppStart() - - driveHeadlessAppStart() - - assertEquals(1, fixture.capturedContexts.size) - val context = fixture.capturedContexts.single() - val options = fixture.capturedOptions.single() - val transaction = fixture.createdTransactions.single() - assertEquals(ActivityLifecycleIntegration.STANDALONE_APP_START_OP, context.operation) - assertEquals("App Start", context.name) - assertEquals(DateUtils.millisToNanos(100), options.startTimestamp!!.nanoTimestamp()) - assertEquals( - transaction.spanContext.traceId, - AppStartMetrics.getInstance().getAppStartTraceId(), - ) - assertTrue(transaction.isFinished) - assertEquals(SpanStatus.OK, transaction.status) - } - @Test fun `onHeadlessAppStart creates standalone App Start transaction when appStartType is WARM`() { val sut = fixture.getSut { diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/AndroidContinuousProfilerTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/AndroidContinuousProfilerTest.kt index b5b4b76e18a..386bbe04eb6 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/AndroidContinuousProfilerTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/AndroidContinuousProfilerTest.kt @@ -302,17 +302,6 @@ class AndroidContinuousProfilerTest { assertEquals(0, profiler.rootSpanCounter) } - @Test - fun `profiler works only on api 22+`() { - val buildInfo = - mock { - whenever(it.sdkInfoVersion).thenReturn(Build.VERSION_CODES.LOLLIPOP) - } - val profiler = fixture.getSut(buildInfo) - profiler.startProfiler(ProfileLifecycle.MANUAL, fixture.mockTracesSampler) - assertFalse(profiler.isRunning) - } - @Test fun `profiler evaluates profilingTracesDirPath options only on first start`() { val profiler = fixture.getSut { it.cacheDirPath = null } diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/AndroidTransactionProfilerTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/AndroidTransactionProfilerTest.kt index 1d2b3a78685..ca9939de84b 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/AndroidTransactionProfilerTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/AndroidTransactionProfilerTest.kt @@ -240,17 +240,6 @@ class AndroidTransactionProfilerTest { assertEquals(profilingTraceData.transactionId, fixture.transaction1.eventId.toString()) } - @Test - fun `profiler works only on api 22+`() { - val buildInfo = - mock { - whenever(it.sdkInfoVersion).thenReturn(Build.VERSION_CODES.LOLLIPOP) - } - val profiler = fixture.getSut(context, buildInfo) - profiler.start() - assertFalse(profiler.isRunning) - } - @Test fun `profiler on profilesSampleRate=0 false`() { fixture.options.apply { profilesSampleRate = 0.0 } diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/NetworkBreadcrumbsIntegrationTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/NetworkBreadcrumbsIntegrationTest.kt index 711f5f7fe0b..8085189b029 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/NetworkBreadcrumbsIntegrationTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/NetworkBreadcrumbsIntegrationTest.kt @@ -94,18 +94,6 @@ class NetworkBreadcrumbsIntegrationTest { assertNull(sut.networkCallback) } - @Test - fun `It doesn't register callback if not on Android N+`() { - val buildInfo = mock() - whenever(buildInfo.sdkInfoVersion).thenReturn(Build.VERSION_CODES.M) - val sut = fixture.getSut(buildInfo = buildInfo) - - sut.register(fixture.scopes, fixture.options) - - assertTrue(AndroidConnectionStatusProvider.getChildCallbacks().isEmpty()) - assertNull(sut.networkCallback) - } - @Test fun `When NetworkBreadcrumbsIntegration is closed, it should unregister the callback`() { val sut = fixture.getSut() diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/ScreenshotEventProcessorTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/ScreenshotEventProcessorTest.kt index 1dfb277175d..e875e7845a0 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/ScreenshotEventProcessorTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/ScreenshotEventProcessorTest.kt @@ -208,7 +208,7 @@ class ScreenshotEventProcessorTest { } @Test - @Config(sdk = [23]) + @Config(sdk = [24]) fun `when screenshot event processor is called from background thread it executes on main thread`() { val sut = fixture.getSut(true) whenever(fixture.threadChecker.isMainThread).thenReturn(false) diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/SentryAndroidTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/SentryAndroidTest.kt index 6d02b678c5e..9b79c5bb56d 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/SentryAndroidTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/SentryAndroidTest.kt @@ -178,6 +178,7 @@ class SentryAndroidTest { fun `set up`() { Sentry.close() AppStartMetrics.getInstance().clear() + ContextUtils.resetInstance() context = ApplicationProvider.getApplicationContext() val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager? fixture.shadowActivityManager = Shadow.extract(activityManager) diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/SentryUserFeedbackFormTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/SentryUserFeedbackFormTest.kt index 840a0772a7f..22a35016e77 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/SentryUserFeedbackFormTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/SentryUserFeedbackFormTest.kt @@ -268,7 +268,7 @@ class SentryUserFeedbackFormTest { val context = ContextThemeWrapper(fixture.application, android.R.style.Theme_Material_Light) assertThat(SentryUserFeedbackForm.resolveTheme(context, 0)) - .isEqualTo(android.R.style.Theme_Material_Light_Dialog_Alert) + .isEqualTo(android.R.style.ThemeOverlay_Material_Dialog_Alert) } @Test diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/internal/gestures/SentryGestureListenerPeekDecorViewTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/internal/gestures/SentryGestureListenerPeekDecorViewTest.kt index 7b26aa55c79..5d6fd79f178 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/internal/gestures/SentryGestureListenerPeekDecorViewTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/internal/gestures/SentryGestureListenerPeekDecorViewTest.kt @@ -2,31 +2,29 @@ package io.sentry.android.core.internal.gestures import android.app.Activity import android.view.MotionEvent +import android.view.Window import androidx.test.ext.junit.runners.AndroidJUnit4 import io.sentry.Breadcrumb import io.sentry.IScopes import io.sentry.android.core.SentryAndroidOptions import io.sentry.util.LazyEvaluator import kotlin.test.Test -import kotlin.test.assertNull import org.junit.runner.RunWith import org.mockito.kotlin.any import org.mockito.kotlin.anyOrNull +import org.mockito.kotlin.doReturn import org.mockito.kotlin.mock import org.mockito.kotlin.never import org.mockito.kotlin.verify -import org.robolectric.Robolectric.buildActivity @RunWith(AndroidJUnit4::class) class SentryGestureListenerPeekDecorViewTest { @Test fun `does not force decor view creation when peekDecorView returns null`() { - // A plain Activity that never calls setContentView — peekDecorView() should return null - val activity = buildActivity(Activity::class.java).create().get() - - // Sanity check: decor view has not been created yet - assertNull(activity.window.peekDecorView()) + // Robolectric creates the decor view in onCreate on API 23+, so mock a window without one + val window = mock() + val activity = mock { on { this.window } doReturn window } val scopes = mock() val options = @@ -39,8 +37,8 @@ class SentryGestureListenerPeekDecorViewTest { val sut = SentryGestureListener(activity, scopes, options) sut.onSingleTapUp(mock()) - // The key assertion: peekDecorView is still null — we did not force view hierarchy creation - assertNull(activity.window.peekDecorView()) + // The key assertion: we did not force view hierarchy creation + verify(window, never()).decorView // And no breadcrumb was captured verify(scopes, never()).addBreadcrumb(any(), anyOrNull()) 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 56a2a12928c..d534ba436d3 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 @@ -662,17 +662,6 @@ class AndroidConnectionStatusProviderTest { ) } - @Test - fun `API level below M falls back to legacy method`() { - whenever(buildInfo.sdkInfoVersion).thenReturn(Build.VERSION_CODES.LOLLIPOP) - whenever(networkInfo.isConnected).thenReturn(true) - - assertEquals( - IConnectionStatusProvider.ConnectionStatus.CONNECTED, - connectionStatusProvider.connectionStatus, - ) - } - @Test fun `onCapabilitiesChanged updates cache`() { whenever(buildInfo.sdkInfoVersion).thenReturn(Build.VERSION_CODES.N) diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/internal/util/CellularNetworkTechnologyProviderTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/internal/util/CellularNetworkTechnologyProviderTest.kt index 4bf1d7de427..29047ee304e 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/internal/util/CellularNetworkTechnologyProviderTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/internal/util/CellularNetworkTechnologyProviderTest.kt @@ -335,14 +335,6 @@ class CellularNetworkTechnologyProviderTest { assertThat(provider.cellularNetworkTechnology).isNull() } - @Test - fun `below Android 7 the technology is not read because getDataNetworkType is unavailable`() { - val provider = fixture.getSut(sdkVersion = Build.VERSION_CODES.M, hasPermission = true) - - assertThat(provider.cellularNetworkTechnology).isNull() - verify(fixture.telephonyManager, never()).dataNetworkType - } - @Test fun `below Android 12 registering does nothing`() { val provider = fixture.getSut(sdkVersion = Build.VERSION_CODES.R) diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/internal/util/SentryFrameMetricsCollectorTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/internal/util/SentryFrameMetricsCollectorTest.kt index 334ce229066..fbd944b7dcf 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/internal/util/SentryFrameMetricsCollectorTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/internal/util/SentryFrameMetricsCollectorTest.kt @@ -117,15 +117,6 @@ class SentryFrameMetricsCollectorTest { } } - @Test - fun `collector works only on api 24+`() { - val buildInfo = - mock { whenever(it.sdkInfoVersion).thenReturn(Build.VERSION_CODES.M) } - val collector = fixture.getSut(context, buildInfo) - val id = collector.startCollection(mock()) - assertNull(id) - } - @Test fun `collector works only if context is instance of Application`() { val collector = fixture.getSut(mock()) @@ -611,9 +602,7 @@ class SentryFrameMetricsCollectorTest { @Test fun `getFramesDelay returns -1 when not available`() { - val buildInfo = - mock { whenever(it.sdkInfoVersion).thenReturn(Build.VERSION_CODES.M) } - val collector = fixture.getSut(context, buildInfo) + val collector = fixture.getSut(mock()) val result = collector.getFramesDelay(0, TimeUnit.SECONDS.toNanos(1)) assertEquals(-1.0, result.delaySeconds) diff --git a/sentry-android-core/src/test/resources/robolectric.properties b/sentry-android-core/src/test/resources/robolectric.properties index c216eeab9dc..64ab54e15fc 100644 --- a/sentry-android-core/src/test/resources/robolectric.properties +++ b/sentry-android-core/src/test/resources/robolectric.properties @@ -1,2 +1,2 @@ -sdk=21 +sdk=24 application=io.sentry.android.core.ApplicationStub diff --git a/sentry-android-integration-tests/sentry-uitest-android/src/main/AndroidManifest.xml b/sentry-android-integration-tests/sentry-uitest-android/src/main/AndroidManifest.xml index 6bcad62e6de..0b676446a85 100644 --- a/sentry-android-integration-tests/sentry-uitest-android/src/main/AndroidManifest.xml +++ b/sentry-android-integration-tests/sentry-uitest-android/src/main/AndroidManifest.xml @@ -9,8 +9,7 @@ android:label="Sentry End2End Tests" android:largeHeap="true" android:icon="@android:mipmap/sym_def_app_icon" - android:usesCleartextTraffic="true" - tools:targetApi="m"> + android:usesCleartextTraffic="true"> diff --git a/sentry-android-integration-tests/sentry-uitest-android/src/main/java/io/sentry/uitest/android/ProfilingSampleActivity.kt b/sentry-android-integration-tests/sentry-uitest-android/src/main/java/io/sentry/uitest/android/ProfilingSampleActivity.kt index fdfeb61e05f..1ac1590e78a 100644 --- a/sentry-android-integration-tests/sentry-uitest-android/src/main/java/io/sentry/uitest/android/ProfilingSampleActivity.kt +++ b/sentry-android-integration-tests/sentry-uitest-android/src/main/java/io/sentry/uitest/android/ProfilingSampleActivity.kt @@ -2,7 +2,6 @@ package io.sentry.uitest.android import android.graphics.Bitmap import android.graphics.Color -import android.os.Build import android.os.Bundle import android.view.LayoutInflater import android.view.ViewGroup @@ -31,9 +30,7 @@ class ProfilingSampleActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - window.setSustainedPerformanceMode(true) - } + window.setSustainedPerformanceMode(true) binding = ActivityProfilingSampleBinding.inflate(layoutInflater) setContentView(binding.root) diff --git a/sentry-android-replay/detekt-baseline.xml b/sentry-android-replay/detekt-baseline.xml index 8432d83f8dd..0567ebb5521 100644 --- a/sentry-android-replay/detekt-baseline.xml +++ b/sentry-android-replay/detekt-baseline.xml @@ -8,7 +8,7 @@ ComplexCondition:ReplayIntegration.kt$ReplayIntegration$state.get().captureStrategy is SessionCaptureStrategy && (lastKnownConnectionStatus == DISCONNECTED || scopes?.rateLimiter?.isActiveForCategory(All) == true || scopes?.rateLimiter?.isActiveForCategory(Replay) == true) ComplexCondition:ScreenshotRecorder.kt$ScreenshotRecorder$root == null || root.width <= 0 || root.height <= 0 || !root.isShown ComplexCondition:SimpleVideoEncoder.kt$SimpleVideoEncoder$Build.MANUFACTURER.contains("xiaomi", ignoreCase = true) || Build.MANUFACTURER.contains("motorola", ignoreCase = true) || SystemProperties.get(SystemProperties.Property.SOC_MANUFACTURER) .equals("spreadtrum", ignoreCase = true) || SystemProperties.get(SystemProperties.Property.SOC_MANUFACTURER) .equals("unisoc", ignoreCase = true) - CyclomaticComplexMethod:CaptureStrategy.kt$CaptureStrategy.Companion$@Suppress("NewApi") private fun buildReplay( options: SentryOptions, video: File, currentReplayId: SentryId, segmentTimestamp: Date, segmentId: Int, height: Int, width: Int, frameCount: Int, frameRate: Int, videoDuration: Long, replayType: ReplayType, screenAtStart: String?, breadcrumbs: List<Breadcrumb>, events: Deque<RRWebEvent>, traceIds: List<String>, segmentNames: List<String>, ): ReplaySegment + CyclomaticComplexMethod:CaptureStrategy.kt$CaptureStrategy.Companion$private fun buildReplay( options: SentryOptions, video: File, currentReplayId: SentryId, segmentTimestamp: Date, segmentId: Int, height: Int, width: Int, frameCount: Int, frameRate: Int, videoDuration: Long, replayType: ReplayType, screenAtStart: String?, breadcrumbs: List<Breadcrumb>, events: Deque<RRWebEvent>, traceIds: List<String>, segmentNames: List<String>, ): ReplaySegment CyclomaticComplexMethod:ComposeViewHierarchyNode.kt$ComposeViewHierarchyNode$private fun fromComposeNode( node: LayoutNode, parent: ViewHierarchyNode?, distance: Int, isComposeRoot: Boolean, options: SentryMaskingOptions, logger: ILogger, ): ViewHierarchyNode? CyclomaticComplexMethod:DefaultReplayBreadcrumbConverter.kt$DefaultReplayBreadcrumbConverter$override fun convert(breadcrumb: Breadcrumb): RRWebEvent? CyclomaticComplexMethod:DefaultReplayBreadcrumbConverter.kt$DefaultReplayBreadcrumbConverter$private fun Breadcrumb.toRRWebSpanEvent(): RRWebSpanEvent @@ -45,7 +45,7 @@ LargeClass:CanvasStrategy.kt$TextIgnoringDelegateCanvas : Canvas LargeClass:ReplayCacheTest.kt$ReplayCacheTest LargeClass:ReplayIntegrationTest.kt$ReplayIntegrationTest - LongMethod:CaptureStrategy.kt$CaptureStrategy.Companion$@Suppress("NewApi") private fun buildReplay( options: SentryOptions, video: File, currentReplayId: SentryId, segmentTimestamp: Date, segmentId: Int, height: Int, width: Int, frameCount: Int, frameRate: Int, videoDuration: Long, replayType: ReplayType, screenAtStart: String?, breadcrumbs: List<Breadcrumb>, events: Deque<RRWebEvent>, traceIds: List<String>, segmentNames: List<String>, ): ReplaySegment + LongMethod:CaptureStrategy.kt$CaptureStrategy.Companion$private fun buildReplay( options: SentryOptions, video: File, currentReplayId: SentryId, segmentTimestamp: Date, segmentId: Int, height: Int, width: Int, frameCount: Int, frameRate: Int, videoDuration: Long, replayType: ReplayType, screenAtStart: String?, breadcrumbs: List<Breadcrumb>, events: Deque<RRWebEvent>, traceIds: List<String>, segmentNames: List<String>, ): ReplaySegment LongMethod:ComposeViewHierarchyNode.kt$ComposeViewHierarchyNode$private fun fromComposeNode( node: LayoutNode, parent: ViewHierarchyNode?, distance: Int, isComposeRoot: Boolean, options: SentryMaskingOptions, logger: ILogger, ): ViewHierarchyNode? LongMethod:DefaultReplayBreadcrumbConverter.kt$DefaultReplayBreadcrumbConverter$override fun convert(breadcrumb: Breadcrumb): RRWebEvent? LongMethod:DefaultReplayBreadcrumbConverter.kt$DefaultReplayBreadcrumbConverter$private fun Breadcrumb.toRRWebSpanEvent(): RRWebSpanEvent @@ -103,7 +103,7 @@ MatchingDeclarationName:Persistable.kt$PersistableLinkedList : ConcurrentLinkedDeque MatchingDeclarationName:ReplayLifecycle.kt$ReplayLifecycleState MaxLineLength:ComposeMaskingOptionsTest.kt$ComposeMaskingOptionsTest$"Flaky: Robolectric intermittently reports zero bounds for nodes, causing isVisible=false and making the assertion non-deterministic" - NestedBlockDepth:CaptureStrategy.kt$CaptureStrategy.Companion$@Suppress("NewApi") private fun buildReplay( options: SentryOptions, video: File, currentReplayId: SentryId, segmentTimestamp: Date, segmentId: Int, height: Int, width: Int, frameCount: Int, frameRate: Int, videoDuration: Long, replayType: ReplayType, screenAtStart: String?, breadcrumbs: List<Breadcrumb>, events: Deque<RRWebEvent>, traceIds: List<String>, segmentNames: List<String>, ): ReplaySegment + NestedBlockDepth:CaptureStrategy.kt$CaptureStrategy.Companion$private fun buildReplay( options: SentryOptions, video: File, currentReplayId: SentryId, segmentTimestamp: Date, segmentId: Int, height: Int, width: Int, frameCount: Int, frameRate: Int, videoDuration: Long, replayType: ReplayType, screenAtStart: String?, breadcrumbs: List<Breadcrumb>, events: Deque<RRWebEvent>, traceIds: List<String>, segmentNames: List<String>, ): ReplaySegment NestedBlockDepth:DefaultReplayBreadcrumbConverter.kt$DefaultReplayBreadcrumbConverter$private fun Breadcrumb.toRRWebSpanEvent(): RRWebSpanEvent NestedBlockDepth:Nodes.kt$internal fun LayoutNode.findPainter(): Painter? NestedBlockDepth:Nodes.kt$internal fun LayoutNode.findTextColor(): Color? diff --git a/sentry-android-replay/src/main/java/io/sentry/android/replay/Windows.kt b/sentry-android-replay/src/main/java/io/sentry/android/replay/Windows.kt index e81c815dc11..4f8f6cabf58 100644 --- a/sentry-android-replay/src/main/java/io/sentry/android/replay/Windows.kt +++ b/sentry-android-replay/src/main/java/io/sentry/android/replay/Windows.kt @@ -123,7 +123,6 @@ internal class RootViewsSpy private constructor() : Closeable { private val delegatingViewList: ArrayList = object : ArrayList() { - @Suppress("NewApi") override fun addAll(elements: Collection): Boolean { listeners.forEach { listener -> elements.forEach { element -> listener.onRootViewsChanged(element, true) } @@ -131,13 +130,11 @@ internal class RootViewsSpy private constructor() : Closeable { return super.addAll(elements) } - @Suppress("NewApi") override fun add(element: View): Boolean { listeners.forEach { it.onRootViewsChanged(element, true) } return super.add(element) } - @Suppress("NewApi") override fun removeAt(index: Int): View { val removedView = super.removeAt(index) listeners.forEach { it.onRootViewsChanged(removedView, false) } @@ -191,7 +188,7 @@ internal object WindowManagerSpy { } // You can discourage me all you want I'll still do it. - @SuppressLint("PrivateApi", "ObsoleteSdkInt", "DiscouragedPrivateApi") + @SuppressLint("PrivateApi", "DiscouragedPrivateApi") fun swapWindowManagerGlobalMViews(swap: (ArrayList) -> ArrayList) { try { windowManagerInstance?.let { windowManagerInstance -> diff --git a/sentry-android-replay/src/main/java/io/sentry/android/replay/capture/CaptureStrategy.kt b/sentry-android-replay/src/main/java/io/sentry/android/replay/capture/CaptureStrategy.kt index fc8b8d13065..ddd9f840c4c 100644 --- a/sentry-android-replay/src/main/java/io/sentry/android/replay/capture/CaptureStrategy.kt +++ b/sentry-android-replay/src/main/java/io/sentry/android/replay/capture/CaptureStrategy.kt @@ -141,7 +141,6 @@ internal interface CaptureStrategy { ) } - @Suppress("NewApi") private fun buildReplay( options: SentryOptions, video: File, @@ -277,7 +276,6 @@ internal interface CaptureStrategy { scopes?.captureReplay(replay, hint.apply { replayRecording = recording }) } - @Suppress("NewApi") fun setSegmentId(segmentId: Int) { replay.segmentId = segmentId recording.payload?.forEach { diff --git a/sentry-android-replay/src/main/java/io/sentry/android/replay/screenshot/CanvasStrategy.kt b/sentry-android-replay/src/main/java/io/sentry/android/replay/screenshot/CanvasStrategy.kt index 6956782cc27..f40d8c8cc5f 100644 --- a/sentry-android-replay/src/main/java/io/sentry/android/replay/screenshot/CanvasStrategy.kt +++ b/sentry-android-replay/src/main/java/io/sentry/android/replay/screenshot/CanvasStrategy.kt @@ -72,7 +72,6 @@ internal class CanvasStrategy( IntegrationUtils.addIntegrationToSdkVersion("ReplayCanvasStrategy") } - @SuppressLint("NewApi") private val pictureRenderTask = Runnable { if (isClosed.get()) { options.logger.log( @@ -152,7 +151,6 @@ internal class CanvasStrategy( } } - @SuppressLint("NewApi") override fun capture(root: View) { if (isClosed.get()) { return diff --git a/sentry-android-replay/src/main/java/io/sentry/android/replay/screenshot/PixelCopyStrategy.kt b/sentry-android-replay/src/main/java/io/sentry/android/replay/screenshot/PixelCopyStrategy.kt index 06be58e19de..4672e7dbfbc 100644 --- a/sentry-android-replay/src/main/java/io/sentry/android/replay/screenshot/PixelCopyStrategy.kt +++ b/sentry-android-replay/src/main/java/io/sentry/android/replay/screenshot/PixelCopyStrategy.kt @@ -222,7 +222,6 @@ internal class PixelCopyStrategy( } } - @SuppressLint("NewApi") private fun captureSurfaceViews( root: View, surfaceViewNodes: List, diff --git a/sentry-android-replay/src/main/java/io/sentry/android/replay/util/FixedWindowCallback.java b/sentry-android-replay/src/main/java/io/sentry/android/replay/util/FixedWindowCallback.java index 7245eefabed..59593e1fad1 100644 --- a/sentry-android-replay/src/main/java/io/sentry/android/replay/util/FixedWindowCallback.java +++ b/sentry-android-replay/src/main/java/io/sentry/android/replay/util/FixedWindowCallback.java @@ -189,7 +189,6 @@ public boolean onSearchRequested() { return delegate.onSearchRequested(); } - @SuppressLint("NewApi") @Override public boolean onSearchRequested(SearchEvent searchEvent) { if (delegate == null) { @@ -207,7 +206,6 @@ public ActionMode onWindowStartingActionMode(ActionMode.Callback callback) { return delegate.onWindowStartingActionMode(callback); } - @SuppressLint("NewApi") @Nullable @Override public ActionMode onWindowStartingActionMode(ActionMode.Callback callback, int type) { @@ -233,7 +231,6 @@ public void onActionModeFinished(ActionMode mode) { delegate.onActionModeFinished(mode); } - @SuppressLint("NewApi") @Override public void onProvideKeyboardShortcuts( List data, @Nullable Menu menu, int deviceId) { diff --git a/sentry-android-replay/src/main/java/io/sentry/android/replay/util/Views.kt b/sentry-android-replay/src/main/java/io/sentry/android/replay/util/Views.kt index cacd2b1c217..a70dafbc3b9 100644 --- a/sentry-android-replay/src/main/java/io/sentry/android/replay/util/Views.kt +++ b/sentry-android-replay/src/main/java/io/sentry/android/replay/util/Views.kt @@ -1,7 +1,6 @@ package io.sentry.android.replay.util import android.annotation.SuppressLint -import android.annotation.TargetApi import android.graphics.Point import android.graphics.Rect import android.graphics.drawable.BitmapDrawable @@ -105,8 +104,6 @@ internal fun View.isVisibleToUser(): Pair { return false to null } -@SuppressLint("ObsoleteSdkInt", "UseRequiresApi") -@TargetApi(21) internal fun Drawable?.isMaskable(): Boolean { // TODO: maybe find a way how to check if the drawable is coming from the apk or loaded from // network diff --git a/sentry-android-replay/src/main/java/io/sentry/android/replay/video/SimpleVideoEncoder.kt b/sentry-android-replay/src/main/java/io/sentry/android/replay/video/SimpleVideoEncoder.kt index b1e3cd20513..e8b77b0994c 100644 --- a/sentry-android-replay/src/main/java/io/sentry/android/replay/video/SimpleVideoEncoder.kt +++ b/sentry-android-replay/src/main/java/io/sentry/android/replay/video/SimpleVideoEncoder.kt @@ -349,8 +349,6 @@ internal fun shouldUseSoftwareCanvas(): Boolean = Build.HARDWARE.contains("goldfish", ignoreCase = true) || Build.HARDWARE.contains("ranchu", ignoreCase = true) -@SuppressLint("UseRequiresApi") -@TargetApi(24) internal data class MuxerConfig( val file: File, var recordingWidth: Int, diff --git a/sentry-android-sqlite/src/test/AndroidManifest.xml b/sentry-android-sqlite/src/test/AndroidManifest.xml deleted file mode 100644 index 967265a1f16..00000000000 --- a/sentry-android-sqlite/src/test/AndroidManifest.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - diff --git a/sentry-samples/sentry-samples-android/build.gradle.kts b/sentry-samples/sentry-samples-android/build.gradle.kts index 441271bc5eb..f832020037d 100644 --- a/sentry-samples/sentry-samples-android/build.gradle.kts +++ b/sentry-samples/sentry-samples-android/build.gradle.kts @@ -48,8 +48,7 @@ android { defaultConfig { applicationId = "io.sentry.samples.android" - // androidx.sqlite 2.6+ require minSdk 23; the Sentry SDK still supports 21. - minSdk = 23 + minSdk = libs.versions.minSdk.get().toInt() targetSdk = libs.versions.targetSdk.get().toInt() versionCode = 2 versionName = project.version.toString()