From e016fedfe7784412698fc88deecd1cd26a221caf Mon Sep 17 00:00:00 2001 From: Nelson Osacky Date: Wed, 4 Nov 2026 14:05:57 +0100 Subject: [PATCH 1/2] ref(android): Remove the enablePerformanceV2 option Performance V2 has defaulted to on for a long time, so 9.0 drops the option and its io.sentry.performance-v2.enable manifest key. Without the flag the v1 paths can't run, so remove them too: the FrameMetricsAggregator-based ActivityFramesTracker, its constructor parameters, and the options parameter of AppStartMetrics.getAppStartTimeSpanWithFallback. SpanFrameMetricsCollector is now always registered. Fixes JAVA-527 Fixes #5472 Co-Authored-By: Claude Opus 5.5 --- .../api/sentry-android-core.api | 16 +- sentry-android-core/proguard-rules.pro | 1 - .../android/core/ActivityFramesTracker.java | 263 ---------- .../core/ActivityLifecycleIntegration.java | 47 +- .../core/AndroidOptionsInitializer.java | 30 +- .../core/DefaultAndroidEventProcessor.java | 2 +- .../android/core/InternalSentrySdk.java | 2 +- .../android/core/ManifestMetadataReader.java | 5 - .../PerformanceAndroidEventProcessor.java | 26 +- .../io/sentry/android/core/SentryAndroid.java | 13 +- .../android/core/SentryAndroidOptions.java | 22 - .../core/SpanFrameMetricsCollector.java | 2 +- .../core/performance/AppStartMetrics.java | 24 +- .../android/core/ActivityFramesTrackerTest.kt | 452 ------------------ .../core/ActivityLifecycleIntegrationTest.kt | 39 +- .../core/AndroidContinuousProfilerTest.kt | 3 - .../core/AndroidOptionsInitializerTest.kt | 118 +---- .../android/core/AndroidProfilerTest.kt | 3 - .../core/AndroidTransactionProfilerTest.kt | 3 - .../core/ManifestMetadataReaderTest.kt | 25 - .../PerformanceAndroidEventProcessorTest.kt | 110 ++--- .../android/core/SentryAndroidOptionsTest.kt | 13 - .../sentry/android/core/SentryAndroidTest.kt | 2 - .../android/core/SentryInitProviderTest.kt | 3 - .../core/SpanFrameMetricsCollectorTest.kt | 1 - .../core/cache/AndroidEnvelopeCacheTest.kt | 8 +- .../core/performance/AppStartMetricsTest.kt | 47 +- .../uitest/android/AutomaticSpansTest.kt | 42 -- .../src/main/AndroidManifest.xml | 4 - 29 files changed, 87 insertions(+), 1239 deletions(-) delete mode 100644 sentry-android-core/src/main/java/io/sentry/android/core/ActivityFramesTracker.java delete mode 100644 sentry-android-core/src/test/java/io/sentry/android/core/ActivityFramesTrackerTest.kt diff --git a/sentry-android-core/api/sentry-android-core.api b/sentry-android-core/api/sentry-android-core.api index 5be86ecddaa..fc385d1415f 100644 --- a/sentry-android-core/api/sentry-android-core.api +++ b/sentry-android-core/api/sentry-android-core.api @@ -11,18 +11,8 @@ public final class io/sentry/android/core/ActivityBreadcrumbsIntegration : andro public fun register (Lio/sentry/IScopes;Lio/sentry/SentryOptions;)V } -public final class io/sentry/android/core/ActivityFramesTracker { - public fun (Lio/sentry/util/LoadClass;Lio/sentry/android/core/SentryAndroidOptions;)V - public fun (Lio/sentry/util/LoadClass;Lio/sentry/android/core/SentryAndroidOptions;Lio/sentry/android/core/MainLooperHandler;)V - public fun addActivity (Landroid/app/Activity;)V - public fun isFrameMetricsAggregatorAvailable ()Z - public fun setMetrics (Landroid/app/Activity;Lio/sentry/protocol/SentryId;)V - public fun stop ()V - public fun takeMetrics (Lio/sentry/protocol/SentryId;)Ljava/util/Map; -} - public final class io/sentry/android/core/ActivityLifecycleIntegration : android/app/Application$ActivityLifecycleCallbacks, io/sentry/Integration, java/io/Closeable { - public fun (Landroid/app/Application;Lio/sentry/android/core/BuildInfoProvider;Lio/sentry/android/core/ActivityFramesTracker;)V + public fun (Landroid/app/Application;Lio/sentry/android/core/BuildInfoProvider;)V public fun close ()V public fun onActivityCreated (Landroid/app/Activity;Landroid/os/Bundle;)V public fun onActivityDestroyed (Landroid/app/Activity;)V @@ -458,7 +448,6 @@ public final class io/sentry/android/core/SentryAndroidOptions : io/sentry/Sentr public fun isEnableNdk ()Z public fun isEnableNdkAppHangTracking ()Z public fun isEnableNetworkEventBreadcrumbs ()Z - public fun isEnablePerformanceV2 ()Z public fun isEnableRootCheck ()Z public fun isEnableScopeSync ()Z public fun isEnableStandaloneAppStartTracing ()Z @@ -493,7 +482,6 @@ public final class io/sentry/android/core/SentryAndroidOptions : io/sentry/Sentr public fun setEnableNdk (Z)V public fun setEnableNdkAppHangTracking (Z)V public fun setEnableNetworkEventBreadcrumbs (Z)V - public fun setEnablePerformanceV2 (Z)V public fun setEnableRootCheck (Z)V public fun setEnableScopeSync (Z)V public fun setEnableStandaloneAppStartTracing (Z)V @@ -813,7 +801,7 @@ public class io/sentry/android/core/performance/AppStartMetrics : io/sentry/andr public fun getAppStartSentryTraceHeader ()Ljava/lang/String; public fun getAppStartTimeSpan ()Lio/sentry/android/core/performance/TimeSpan; public fun getAppStartTimeSpanForHeadless ()Lio/sentry/android/core/performance/TimeSpan; - public fun getAppStartTimeSpanWithFallback (Lio/sentry/android/core/SentryAndroidOptions;)Lio/sentry/android/core/performance/TimeSpan; + public fun getAppStartTimeSpanWithFallback ()Lio/sentry/android/core/performance/TimeSpan; public fun getAppStartTraceId ()Lio/sentry/protocol/SentryId; public fun getAppStartType ()Lio/sentry/android/core/performance/AppStartMetrics$AppStartType; public fun getApplicationOnCreateTimeSpan ()Lio/sentry/android/core/performance/TimeSpan; diff --git a/sentry-android-core/proguard-rules.pro b/sentry-android-core/proguard-rules.pro index eadc6917af7..73c5cf68360 100644 --- a/sentry-android-core/proguard-rules.pro +++ b/sentry-android-core/proguard-rules.pro @@ -1,7 +1,6 @@ ##---------------Begin: proguard configuration for android-core ---------- ##---------------Begin: proguard configuration for androidx.core ---------- --keep class androidx.core.app.FrameMetricsAggregator { (...); } -keep interface androidx.core.view.ScrollingView { *; } ##---------------End: proguard configuration for androidx.core ---------- diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/ActivityFramesTracker.java b/sentry-android-core/src/main/java/io/sentry/android/core/ActivityFramesTracker.java deleted file mode 100644 index 3895819fc94..00000000000 --- a/sentry-android-core/src/main/java/io/sentry/android/core/ActivityFramesTracker.java +++ /dev/null @@ -1,263 +0,0 @@ -package io.sentry.android.core; - -import android.app.Activity; -import android.util.SparseIntArray; -import androidx.core.app.FrameMetricsAggregator; -import io.sentry.ISentryLifecycleToken; -import io.sentry.MeasurementUnit; -import io.sentry.SentryLevel; -import io.sentry.android.core.internal.util.AndroidThreadChecker; -import io.sentry.protocol.MeasurementValue; -import io.sentry.protocol.SentryId; -import io.sentry.util.AutoClosableReentrantLock; -import io.sentry.util.LazyEvaluator; -import java.util.HashMap; -import java.util.Map; -import java.util.WeakHashMap; -import java.util.concurrent.ConcurrentHashMap; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.annotations.TestOnly; -import org.jetbrains.annotations.VisibleForTesting; - -/** - * A class that tracks slow and frozen frames using the FrameMetricsAggregator class from - * androidx.core package. It also checks if the FrameMetricsAggregator class is available at - * runtime. - * - *

If performance-v2 is enabled, frame metrics are recorded using {@link - * io.sentry.android.core.internal.util.SentryFrameMetricsCollector} via {@link - * SpanFrameMetricsCollector} instead and this implementation will no-op. - */ -public final class ActivityFramesTracker { - - private @NotNull LazyEvaluator frameMetricsAggregator; - private @NotNull final SentryAndroidOptions options; - - private final @NotNull Map> - activityMeasurements = new ConcurrentHashMap<>(); - private final @NotNull Map frameCountAtStartSnapshots = - new WeakHashMap<>(); - - private final @NotNull MainLooperHandler handler; - protected @NotNull AutoClosableReentrantLock lock = new AutoClosableReentrantLock(); - - private final @NotNull LazyEvaluator androidXAvailable; - - public ActivityFramesTracker( - final @NotNull io.sentry.util.LoadClass loadClass, - final @NotNull SentryAndroidOptions options, - final @NotNull MainLooperHandler handler) { - - androidXAvailable = - loadClass.isClassAvailableLazy( - "androidx.core.app.FrameMetricsAggregator", options.getLogger()); - frameMetricsAggregator = new LazyEvaluator<>(() -> new FrameMetricsAggregator()); - - this.options = options; - this.handler = handler; - } - - public ActivityFramesTracker( - final @NotNull io.sentry.util.LoadClass loadClass, - final @NotNull SentryAndroidOptions options) { - this(loadClass, options, new MainLooperHandler()); - } - - @TestOnly - ActivityFramesTracker( - final @NotNull io.sentry.util.LoadClass loadClass, - final @NotNull SentryAndroidOptions options, - final @NotNull MainLooperHandler handler, - final @NotNull FrameMetricsAggregator frameMetricsAggregator) { - - this(loadClass, options, handler); - this.frameMetricsAggregator = new LazyEvaluator<>(() -> frameMetricsAggregator); - } - - @VisibleForTesting - public boolean isFrameMetricsAggregatorAvailable() { - return androidXAvailable.getValue() - && options.isEnableFramesTracking() - && !options.isEnablePerformanceV2(); - } - - @SuppressWarnings("NullAway") - public void addActivity(final @NotNull Activity activity) { - try (final @NotNull ISentryLifecycleToken ignored = lock.acquire()) { - if (!isFrameMetricsAggregatorAvailable()) { - return; - } - - runSafelyOnUiThread( - () -> frameMetricsAggregator.getValue().add(activity), "FrameMetricsAggregator.add"); - snapshotFrameCountsAtStart(activity); - } - } - - private void snapshotFrameCountsAtStart(final @NotNull Activity activity) { - FrameCounts frameCounts = calculateCurrentFrameCounts(); - if (frameCounts != null) { - frameCountAtStartSnapshots.put(activity, frameCounts); - } - } - - private @Nullable FrameCounts calculateCurrentFrameCounts() { - if (!isFrameMetricsAggregatorAvailable()) { - return null; - } - - if (!androidXAvailable.getValue()) { - return null; - } - - final @Nullable SparseIntArray[] framesRates = frameMetricsAggregator.getValue().getMetrics(); - - int totalFrames = 0; - int slowFrames = 0; - int frozenFrames = 0; - - if (framesRates != null && framesRates.length > 0) { - final SparseIntArray totalIndexArray = framesRates[FrameMetricsAggregator.TOTAL_INDEX]; - if (totalIndexArray != null) { - for (int i = 0; i < totalIndexArray.size(); i++) { - int frameTime = totalIndexArray.keyAt(i); - int numFrames = totalIndexArray.valueAt(i); - totalFrames += numFrames; - // hard coded values, its also in the official android docs and frame metrics API - if (frameTime > 700) { - // frozen frames, threshold is 700ms - frozenFrames += numFrames; - } else if (frameTime > 16) { - // slow frames, above 16ms, 60 frames/second - slowFrames += numFrames; - } - } - } - } - - return new FrameCounts(totalFrames, slowFrames, frozenFrames); - } - - @SuppressWarnings("NullAway") - public void setMetrics(final @NotNull Activity activity, final @NotNull SentryId transactionId) { - try (final @NotNull ISentryLifecycleToken ignored = lock.acquire()) { - if (!isFrameMetricsAggregatorAvailable()) { - return; - } - - // NOTE: removing an activity does not reset the frame counts, only reset() does - // throws IllegalArgumentException when attempting to remove - // OnFrameMetricsAvailableListener - // that was never added. - // there's no contains method. - // throws NullPointerException when attempting to remove - // OnFrameMetricsAvailableListener and - // there was no - // Observers, See - // https://android.googlesource.com/platform/frameworks/base/+/140ff5ea8e2d99edc3fbe63a43239e459334c76b - runSafelyOnUiThread(() -> frameMetricsAggregator.getValue().remove(activity), null); - - final @Nullable FrameCounts frameCounts = diffFrameCountsAtEnd(activity); - - if (frameCounts == null - || (frameCounts.totalFrames == 0 - && frameCounts.slowFrames == 0 - && frameCounts.frozenFrames == 0)) { - return; - } - - final MeasurementValue tfValues = - new MeasurementValue(frameCounts.totalFrames, MeasurementUnit.NONE); - final MeasurementValue sfValues = - new MeasurementValue(frameCounts.slowFrames, MeasurementUnit.NONE); - final MeasurementValue ffValues = - new MeasurementValue(frameCounts.frozenFrames, MeasurementUnit.NONE); - final Map measurements = new HashMap<>(); - measurements.put(MeasurementValue.KEY_FRAMES_TOTAL, tfValues); - measurements.put(MeasurementValue.KEY_FRAMES_SLOW, sfValues); - measurements.put(MeasurementValue.KEY_FRAMES_FROZEN, ffValues); - - activityMeasurements.put(transactionId, measurements); - } - } - - private @Nullable FrameCounts diffFrameCountsAtEnd(final @NotNull Activity activity) { - @Nullable final FrameCounts frameCountsAtStart = frameCountAtStartSnapshots.remove(activity); - if (frameCountsAtStart == null) { - return null; - } - - @Nullable final FrameCounts frameCountsAtEnd = calculateCurrentFrameCounts(); - if (frameCountsAtEnd == null) { - return null; - } - - final int diffTotalFrames = frameCountsAtEnd.totalFrames - frameCountsAtStart.totalFrames; - final int diffSlowFrames = frameCountsAtEnd.slowFrames - frameCountsAtStart.slowFrames; - final int diffFrozenFrames = frameCountsAtEnd.frozenFrames - frameCountsAtStart.frozenFrames; - - return new FrameCounts(diffTotalFrames, diffSlowFrames, diffFrozenFrames); - } - - @Nullable - public Map takeMetrics(final @NotNull SentryId transactionId) { - try (final @NotNull ISentryLifecycleToken ignored = lock.acquire()) { - if (!isFrameMetricsAggregatorAvailable()) { - return null; - } - - final Map stringMeasurementValueMap = - activityMeasurements.get(transactionId); - activityMeasurements.remove(transactionId); - return stringMeasurementValueMap; - } - } - - @SuppressWarnings("NullAway") - public void stop() { - try (final @NotNull ISentryLifecycleToken ignored = lock.acquire()) { - if (isFrameMetricsAggregatorAvailable()) { - runSafelyOnUiThread( - () -> frameMetricsAggregator.getValue().stop(), "FrameMetricsAggregator.stop"); - frameMetricsAggregator.getValue().reset(); - } - activityMeasurements.clear(); - } - } - - private void runSafelyOnUiThread(final Runnable runnable, final String tag) { - try { - if (AndroidThreadChecker.getInstance().isMainThread()) { - runnable.run(); - } else { - handler.post( - () -> { - try { - runnable.run(); - } catch (Throwable ignored) { - if (tag != null) { - options.getLogger().log(SentryLevel.WARNING, "Failed to execute " + tag); - } - } - }); - } - } catch (Throwable ignored) { - if (tag != null) { - options.getLogger().log(SentryLevel.WARNING, "Failed to execute " + tag); - } - } - } - - private static final class FrameCounts { - private final int totalFrames; - private final int slowFrames; - private final int frozenFrames; - - private FrameCounts(final int totalFrames, final int slowFrames, final int frozenFrames) { - this.totalFrames = totalFrames; - this.slowFrames = slowFrames; - this.frozenFrames = frozenFrames; - } - } -} 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 05bd9db9642..4ab9be82248 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 @@ -42,7 +42,6 @@ import io.sentry.util.TracingUtils; import java.io.Closeable; import java.io.IOException; -import java.lang.ref.WeakReference; import java.util.Collections; import java.util.Map; import java.util.WeakHashMap; @@ -104,21 +103,16 @@ public final class ActivityLifecycleIntegration private final @NotNull WeakHashMap activitiesWithOngoingTransactions = new WeakHashMap<>(); - private final @NotNull ActivityFramesTracker activityFramesTracker; private final @NotNull AutoClosableReentrantLock lock = new AutoClosableReentrantLock(); private boolean fullyDisplayedCalled = false; private final @NotNull AutoClosableReentrantLock fullyDisplayedLock = new AutoClosableReentrantLock(); public ActivityLifecycleIntegration( - final @NotNull Application application, - final @NotNull BuildInfoProvider buildInfoProvider, - final @NotNull ActivityFramesTracker activityFramesTracker) { + final @NotNull Application application, final @NotNull BuildInfoProvider buildInfoProvider) { this.application = Objects.requireNonNull(application, "Application is required"); this.buildInfoProvider = Objects.requireNonNull(buildInfoProvider, "BuildInfoProvider is required"); - this.activityFramesTracker = - Objects.requireNonNull(activityFramesTracker, "ActivityFramesTracker is required"); if (buildInfoProvider.getSdkInfoVersion() >= Build.VERSION_CODES.Q) { isAllActivityCallbacksAvailable = true; @@ -165,8 +159,6 @@ public void close() throws IOException { if (options != null) { options.getLogger().log(SentryLevel.DEBUG, "ActivityLifecycleIntegration removed."); } - - activityFramesTracker.stop(); } private @NotNull String getActivityName(final @NotNull Activity activity) { @@ -184,7 +176,6 @@ private void stopPreviousTransactions() { } private void startTracing(final @NotNull Activity activity) { - WeakReference weakActivity = new WeakReference<>(activity); if (scopes != null && !isRunningTransactionOrTrace(activity)) { if (!performanceEnabled) { activitiesWithOngoingTransactions.put(activity, NoOpTransaction.getInstance()); @@ -200,7 +191,7 @@ private void startTracing(final @NotNull Activity activity) { final @Nullable SentryDate appStartTime; final @Nullable Boolean coldStart; final @NotNull TimeSpan appStartTimeSpan = - AppStartMetrics.getInstance().getAppStartTimeSpanWithFallback(options); + AppStartMetrics.getInstance().getAppStartTimeSpanWithFallback(); // we only track app start for processes that will show an Activity (full launch). // Here we check the process importance which will tell us that. @@ -227,23 +218,6 @@ private void startTracing(final @NotNull Activity activity) { transactionOptions.setTrimEnd(true); } transactionOptions.setWaitForChildren(true); - transactionOptions.setTransactionFinishedCallback( - (finishingTransaction) -> { - @Nullable Activity unwrappedActivity = weakActivity.get(); - if (unwrappedActivity != null) { - activityFramesTracker.setMetrics( - unwrappedActivity, finishingTransaction.getEventId()); - } else { - if (options != null) { - options - .getLogger() - .log( - SentryLevel.WARNING, - "Unable to track activity frames as the Activity %s has been destroyed.", - activityName); - } - } - }); // This will be the start timestamp of the transaction, as well as the ttid/ttfd spans final @NotNull SentryDate ttidStartTime; @@ -611,15 +585,6 @@ public void onActivityStarted(final @NotNull Activity activity) { onActivityPostCreated(activity, null); onActivityPreStarted(activity); } - if (performanceEnabled) { - // The docs on the screen rendering performance tracing - // (https://firebase.google.com/docs/perf-mon/screen-traces?platform=android#definition), - // state that the tracing starts for every Activity class when the app calls - // .onActivityStarted. - // Adding an Activity in onActivityCreated leads to Window.FEATURE_NO_TITLE not - // working. Moving this to onActivityStarted fixes the problem. - activityFramesTracker.addActivity(activity); - } } } @@ -896,12 +861,6 @@ void setFirstActivityCreated(boolean firstActivityCreated) { this.firstActivityCreated = firstActivityCreated; } - @TestOnly - @NotNull - ActivityFramesTracker getActivityFramesTracker() { - return activityFramesTracker; - } - @TestOnly @Nullable ISpan getAppStartSpan() { @@ -970,7 +929,7 @@ private void finishAppStartSpan(final @Nullable SentryDate endDate) { endDate != null ? endDate : AppStartMetrics.getInstance() - .getAppStartTimeSpanWithFallback(options) + .getAppStartTimeSpanWithFallback() .getProjectedStopTimestamp(); if (performanceEnabled && appStartEndTime != null) { finishSpan(appStartSpan, appStartEndTime); diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/AndroidOptionsInitializer.java b/sentry-android-core/src/main/java/io/sentry/android/core/AndroidOptionsInitializer.java index 83f9d9b3492..1da11b21880 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/AndroidOptionsInitializer.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/AndroidOptionsInitializer.java @@ -150,15 +150,9 @@ static void initializeIntegrationsAndProcessors( final @NotNull SentryAndroidOptions options, final @NotNull Context context, final @NotNull io.sentry.util.LoadClass loadClass, - final @NotNull ActivityFramesTracker activityFramesTracker, final boolean isReplayAvailable) { initializeIntegrationsAndProcessors( - options, - context, - new BuildInfoProvider(new AndroidLogger()), - loadClass, - activityFramesTracker, - isReplayAvailable); + options, context, new BuildInfoProvider(new AndroidLogger()), loadClass, isReplayAvailable); } static void initializeIntegrationsAndProcessors( @@ -166,7 +160,6 @@ static void initializeIntegrationsAndProcessors( final @NotNull Context context, final @NotNull BuildInfoProvider buildInfoProvider, final @NotNull io.sentry.util.LoadClass loadClass, - final @NotNull ActivityFramesTracker activityFramesTracker, final boolean isReplayAvailable) { if (options.getCacheDirPath() != null @@ -193,7 +186,7 @@ static void initializeIntegrationsAndProcessors( options.addEventProcessor(new DeduplicateMultithreadedEventProcessor(options)); options.addEventProcessor( new DefaultAndroidEventProcessor(context, buildInfoProvider, options)); - options.addEventProcessor(new PerformanceAndroidEventProcessor(options, activityFramesTracker)); + options.addEventProcessor(new PerformanceAndroidEventProcessor(options)); options.addEventProcessor( new ScreenshotEventProcessor(options, buildInfoProvider, isReplayAvailable)); options.addEventProcessor(new ViewHierarchyEventProcessor(options)); @@ -264,15 +257,12 @@ static void initializeIntegrationsAndProcessors( if (options.getPerformanceCollectors().isEmpty()) { options.addPerformanceCollector(new AndroidMemoryCollector()); options.addPerformanceCollector(new AndroidCpuCollector(options.getLogger())); - - if (options.isEnablePerformanceV2()) { - options.addPerformanceCollector( - new SpanFrameMetricsCollector( - options, - Objects.requireNonNull( - options.getFrameMetricsCollector(), - "options.getFrameMetricsCollector is required"))); - } + options.addPerformanceCollector( + new SpanFrameMetricsCollector( + options, + Objects.requireNonNull( + options.getFrameMetricsCollector(), + "options.getFrameMetricsCollector is required"))); } if (options.getCompositePerformanceCollector() instanceof NoOpCompositePerformanceCollector) { options.setCompositePerformanceCollector(new DefaultCompositePerformanceCollector(options)); @@ -414,7 +404,6 @@ static void installDefaultIntegrations( final @NotNull SentryAndroidOptions options, final @NotNull BuildInfoProvider buildInfoProvider, final @NotNull io.sentry.util.LoadClass loadClass, - final @NotNull ActivityFramesTracker activityFramesTracker, final boolean isFragmentAvailable, final boolean isTimberAvailable, final boolean isReplayAvailable, @@ -470,8 +459,7 @@ static void installDefaultIntegrations( // registerActivityLifecycleCallbacks is only available if Context is an AppContext if (context instanceof Application) { options.addIntegration( - new ActivityLifecycleIntegration( - (Application) context, buildInfoProvider, activityFramesTracker)); + new ActivityLifecycleIntegration((Application) context, buildInfoProvider)); options.addIntegration(new ActivityBreadcrumbsIntegration((Application) context)); options.addIntegration(new UserInteractionIntegration((Application) context, loadClass)); options.addIntegration(new FeedbackShakeIntegration((Application) context)); 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..b6fb245c11d 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 @@ -352,7 +352,7 @@ private void setDist(final @NotNull SentryBaseEvent event, final @NotNull String private void setAppExtras(final @NotNull App app, final @NotNull Hint hint) { app.setAppName(ContextUtils.getApplicationName(context)); final @NotNull TimeSpan appStartTimeSpan = - AppStartMetrics.getInstance().getAppStartTimeSpanWithFallback(options); + AppStartMetrics.getInstance().getAppStartTimeSpanWithFallback(); if (appStartTimeSpan.hasStarted()) { app.setAppStartTime(DateUtils.toUtilDate(appStartTimeSpan.getStartTimestamp())); } diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/InternalSentrySdk.java b/sentry-android-core/src/main/java/io/sentry/android/core/InternalSentrySdk.java index a91225f32c1..4ad11399d02 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/InternalSentrySdk.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/InternalSentrySdk.java @@ -116,7 +116,7 @@ public static Map serializeScope( app.setAppName(ContextUtils.getApplicationName(context)); final @NotNull TimeSpan appStartTimeSpan = - AppStartMetrics.getInstance().getAppStartTimeSpanWithFallback(options); + AppStartMetrics.getInstance().getAppStartTimeSpanWithFallback(); if (appStartTimeSpan.hasStarted()) { app.setAppStartTime(DateUtils.toUtilDate(appStartTimeSpan.getStartTimestamp())); } diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/ManifestMetadataReader.java b/sentry-android-core/src/main/java/io/sentry/android/core/ManifestMetadataReader.java index b6e802bbd5f..7695d86f0be 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/ManifestMetadataReader.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/ManifestMetadataReader.java @@ -143,8 +143,6 @@ final class ManifestMetadataReader { static final String SEND_MODULES = "io.sentry.send-modules"; - static final String ENABLE_PERFORMANCE_V2 = "io.sentry.performance-v2.enable"; - static final String ENABLE_STANDALONE_APP_START_TRACING = "io.sentry.standalone-app-start-tracing.enable"; @@ -578,9 +576,6 @@ static void applyMetadata( options.setSendModules(readBool(metadata, logger, SEND_MODULES, options.isSendModules())); - options.setEnablePerformanceV2( - readBool(metadata, logger, ENABLE_PERFORMANCE_V2, options.isEnablePerformanceV2())); - options.setEnableStandaloneAppStartTracing( readBool( metadata, diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/PerformanceAndroidEventProcessor.java b/sentry-android-core/src/main/java/io/sentry/android/core/PerformanceAndroidEventProcessor.java index 142bb1f4752..aff50c99340 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/PerformanceAndroidEventProcessor.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/PerformanceAndroidEventProcessor.java @@ -5,7 +5,6 @@ import static io.sentry.android.core.ActivityLifecycleIntegration.APP_START_TYPE_DATA; import static io.sentry.android.core.ActivityLifecycleIntegration.APP_START_WARM; import static io.sentry.android.core.ActivityLifecycleIntegration.STANDALONE_APP_START_OP; -import static io.sentry.android.core.ActivityLifecycleIntegration.UI_LOAD_OP; import io.sentry.Hint; import io.sentry.ISentryLifecycleToken; @@ -48,16 +47,11 @@ final class PerformanceAndroidEventProcessor implements SentryEventProcessor { private boolean sentStartMeasurement = false; - private final @NotNull ActivityFramesTracker activityFramesTracker; private final @NotNull SentryAndroidOptions options; private final @NotNull AutoClosableReentrantLock lock = new AutoClosableReentrantLock(); - PerformanceAndroidEventProcessor( - final @NotNull SentryAndroidOptions options, - final @NotNull ActivityFramesTracker activityFramesTracker) { + PerformanceAndroidEventProcessor(final @NotNull SentryAndroidOptions options) { this.options = Objects.requireNonNull(options, "SentryAndroidOptions is required"); - this.activityFramesTracker = - Objects.requireNonNull(activityFramesTracker, "ActivityFramesTracker is required"); } /** @@ -107,7 +101,7 @@ public SentryEvent process(@NotNull SentryEvent event, @NotNull Hint hint) { final @NotNull TimeSpan appStartTimeSpan = isHeadlessStandaloneAppStartTxn ? appStartMetrics.getAppStartTimeSpanForHeadless() - : appStartMetrics.getAppStartTimeSpanWithFallback(options); + : appStartMetrics.getAppStartTimeSpanWithFallback(); final long naturalDurationMs = appStartTimeSpan.getDurationMs(); final long appStartUpDurationMs; @@ -173,22 +167,6 @@ public SentryEvent process(@NotNull SentryEvent event, @NotNull Hint hint) { setContributingFlags(transaction); - final SentryId eventId = transaction.getEventId(); - final SpanContext spanContext = transaction.getContexts().getTrace(); - - // only add slow/frozen frames to transactions created by ActivityLifecycleIntegration - // which have the operation UI_LOAD_OP. If a user-defined (or hybrid SDK) transaction - // users it, we'll also add the metrics if available - if (eventId != null - && spanContext != null - && spanContext.getOperation().contentEquals(UI_LOAD_OP)) { - final Map framesMetrics = - activityFramesTracker.takeMetrics(eventId); - if (framesMetrics != null) { - transaction.getMeasurements().putAll(framesMetrics); - } - } - return transaction; } } 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 82916a248e6..e649e9e094c 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 @@ -122,8 +122,6 @@ public static void init( final BuildInfoProvider buildInfoProvider = new BuildInfoProvider(logger); final io.sentry.util.LoadClass loadClass = new io.sentry.util.LoadClass(); - final ActivityFramesTracker activityFramesTracker = - new ActivityFramesTracker(loadClass, options); AndroidOptionsInitializer.loadDefaultAndMetadataOptions( options, context, logger, buildInfoProvider); @@ -136,7 +134,6 @@ public static void init( options, buildInfoProvider, loadClass, - activityFramesTracker, isFragmentAvailable, isTimberAvailable, isReplayAvailable, @@ -157,8 +154,7 @@ 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 (options.isEnablePerformanceV2() - && buildInfoProvider.getSdkInfoVersion() >= android.os.Build.VERSION_CODES.N) { + if (buildInfoProvider.getSdkInfoVersion() >= android.os.Build.VERSION_CODES.N) { final @NotNull TimeSpan appStartTimeSpan = appStartMetrics.getAppStartTimeSpan(); if (appStartTimeSpan.hasNotStarted()) { appStartTimeSpan.setStartedAt(Process.getStartUptimeMillis()); @@ -174,12 +170,7 @@ public static void init( } AndroidOptionsInitializer.initializeIntegrationsAndProcessors( - options, - context, - buildInfoProvider, - loadClass, - activityFramesTracker, - isReplayAvailable); + options, context, buildInfoProvider, loadClass, isReplayAvailable); deduplicateIntegrations(options, isFragmentAvailable, isTimberAvailable); }, diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroidOptions.java b/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroidOptions.java index e80d3b64855..46dc16e0410 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroidOptions.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroidOptions.java @@ -303,8 +303,6 @@ public interface BeforeCaptureCallback { */ private boolean attachRawTombstone = false; - private boolean enablePerformanceV2 = true; - private boolean enableStandaloneAppStartTracing = false; private @Nullable SentryFrameMetricsCollector frameMetricsCollector; @@ -868,26 +866,6 @@ public void setReportHistoricalMemoryLimiterExits( this.reportHistoricalMemoryLimiterExits = reportHistoricalMemoryLimiterExits; } - /** - * @return true if performance-v2 is enabled. See {@link #setEnablePerformanceV2(boolean)} for - * more details. - */ - public boolean isEnablePerformanceV2() { - return enablePerformanceV2; - } - - /** - * Enables or disables the Performance V2 SDK features. - * - *

With this change - Cold app start spans will provide more accurate timings - Cold app start - * spans will be enriched with detailed ContentProvider, Application and Activity startup times - * - * @param enablePerformanceV2 true if enabled or false otherwise - */ - public void setEnablePerformanceV2(final boolean enablePerformanceV2) { - this.enablePerformanceV2 = enablePerformanceV2; - } - /** * @return true if standalone app start tracing is enabled. See {@link * #setEnableStandaloneAppStartTracing(boolean)} for more details. diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/SpanFrameMetricsCollector.java b/sentry-android-core/src/main/java/io/sentry/android/core/SpanFrameMetricsCollector.java index 074a4a6ea51..612e6809ab4 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/SpanFrameMetricsCollector.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/SpanFrameMetricsCollector.java @@ -73,7 +73,7 @@ public SpanFrameMetricsCollector( final @NotNull SentryFrameMetricsCollector frameMetricsCollector) { this.frameMetricsCollector = frameMetricsCollector; - enabled = options.isEnablePerformanceV2() && options.isEnableFramesTracking(); + enabled = options.isEnableFramesTracking(); } @Override 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 dacab11e4f4..a23b9a8720b 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 @@ -26,7 +26,6 @@ import io.sentry.android.core.BuildInfoProvider; import io.sentry.android.core.ContextUtils; import io.sentry.android.core.CurrentActivityHolder; -import io.sentry.android.core.SentryAndroidOptions; import io.sentry.android.core.internal.util.FirstDrawDoneListener; import io.sentry.protocol.SentryId; import io.sentry.util.AutoClosableReentrantLock; @@ -145,8 +144,8 @@ public AppStartMetrics() { } /** - * @return the SDK init time span, as measured pre-performance-v2 Uses ContentProvider/Sdk init - * time as start timestamp + * @return the SDK init time span. Uses ContentProvider/Sdk init time as start timestamp. It's the + * app start fallback below API 24, where the process start time isn't available. *

Data is filled by either {@link io.sentry.android.core.SentryPerformanceProvider} with a * fallback to {@link io.sentry.android.core.SentryAndroid}. At least the start timestamp * should always be set. @@ -339,21 +338,18 @@ public long getClassLoadedUptimeMs() { } /** - * @return the app start time span if it was started and perf-2 is enabled, falls back to the sdk - * init time span otherwise + * @return the app start time span if it was started, falls back to the sdk init time span + * otherwise */ - public @NotNull TimeSpan getAppStartTimeSpanWithFallback( - final @NotNull SentryAndroidOptions options) { + public @NotNull TimeSpan getAppStartTimeSpanWithFallback() { // If the app start type was never determined or app wasn't launched in foreground, // the app start is considered invalid if (appStartType != AppStartType.UNKNOWN && isAppLaunchedInForeground()) { - if (options.isEnablePerformanceV2()) { - // Only started when sdk version is >= N - final @NotNull TimeSpan appStartSpan = getAppStartTimeSpan(); - if (appStartSpan.hasStarted() - && appStartSpan.getDurationMs() <= TimeUnit.MINUTES.toMillis(1)) { - return appStartSpan; - } + // Only started when sdk version is >= N + final @NotNull TimeSpan appStartSpan = getAppStartTimeSpan(); + if (appStartSpan.hasStarted() + && appStartSpan.getDurationMs() <= TimeUnit.MINUTES.toMillis(1)) { + return appStartSpan; } // fallback: use sdk init time span, as it will always have a start time set diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/ActivityFramesTrackerTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/ActivityFramesTrackerTest.kt deleted file mode 100644 index 55af0d71b0e..00000000000 --- a/sentry-android-core/src/test/java/io/sentry/android/core/ActivityFramesTrackerTest.kt +++ /dev/null @@ -1,452 +0,0 @@ -package io.sentry.android.core - -import android.app.Activity -import android.util.SparseIntArray -import androidx.core.app.FrameMetricsAggregator -import androidx.test.ext.junit.runners.AndroidJUnit4 -import io.sentry.ILogger -import io.sentry.SentryOptions -import io.sentry.protocol.MeasurementValue -import io.sentry.protocol.SentryId -import io.sentry.util.LazyEvaluator -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertFalse -import kotlin.test.assertNull -import kotlin.test.assertTrue -import org.junit.runner.RunWith -import org.mockito.kotlin.any -import org.mockito.kotlin.mock -import org.mockito.kotlin.times -import org.mockito.kotlin.verify -import org.mockito.kotlin.whenever - -@RunWith(AndroidJUnit4::class) -class ActivityFramesTrackerTest { - private class Fixture { - val aggregator = mock() - val activity = mock() - val sentryId = SentryId() - val loadClass = mock() - val handler = mock() - val options = SentryAndroidOptions() - - init { - // ActivityFramesTracker is used only if performanceV2 is disabled - options.isEnablePerformanceV2 = false - } - - fun getSut(isAndroidxAvailable: Boolean = true): ActivityFramesTracker { - whenever(loadClass.isClassAvailableLazy(any(), any())) - .thenReturn(LazyEvaluator { isAndroidxAvailable }) - whenever(loadClass.isClassAvailableLazy(any(), any())) - .thenReturn(LazyEvaluator { isAndroidxAvailable }) - - return ActivityFramesTracker(loadClass, options, handler, aggregator) - } - } - - private val fixture = Fixture() - - @Test - fun `sets total frames`() { - val sut = fixture.getSut() - val array = getArray() - - whenever(fixture.aggregator.metrics).thenReturn(emptyArray(), array) - - sut.addActivity(fixture.activity) - sut.setMetrics(fixture.activity, fixture.sentryId) - - val metrics = sut.takeMetrics(fixture.sentryId) - val totalFrames = metrics!![MeasurementValue.KEY_FRAMES_TOTAL] - - assertEquals(totalFrames!!.value, 1) - assertEquals(totalFrames.unit, "none") - } - - @Test - fun `sets frozen frames`() { - val sut = fixture.getSut() - val arrayAtStart = getArray(frameTime = 701, numFrames = 5) - val arrayAtEnd = getArray(frameTime = 701, numFrames = 10) - - whenever(fixture.aggregator.metrics).thenReturn(arrayAtStart, arrayAtEnd) - - sut.addActivity(fixture.activity) - sut.setMetrics(fixture.activity, fixture.sentryId) - - val metrics = sut.takeMetrics(fixture.sentryId) - val frozenFrames = metrics!![MeasurementValue.KEY_FRAMES_FROZEN] - - assertEquals(frozenFrames!!.value, 5) - assertEquals(frozenFrames.unit, "none") - } - - @Test - fun `sets slow frames`() { - val sut = fixture.getSut() - val arrayAtStart = getArray(frameTime = 20, numFrames = 5) - val arrayAtEnd = getArray(frameTime = 20, numFrames = 10) - - whenever(fixture.aggregator.metrics).thenReturn(arrayAtStart, arrayAtEnd) - - sut.addActivity(fixture.activity) - sut.setMetrics(fixture.activity, fixture.sentryId) - - val metrics = sut.takeMetrics(fixture.sentryId) - val slowFrames = metrics!![MeasurementValue.KEY_FRAMES_SLOW] - - assertEquals(slowFrames!!.value, 5) - assertEquals(slowFrames.unit, "none") - } - - @Test - fun `sets slow and frozen frames`() { - val sut = fixture.getSut() - val array = - SparseIntArray() - .also { - it.put(16, 100) - it.put(20, 5) - it.put(701, 6) - } - .let { arrayOf(it) } - - whenever(fixture.aggregator.metrics).thenReturn(emptyArray(), array) - - sut.addActivity(fixture.activity) - sut.setMetrics(fixture.activity, fixture.sentryId) - - val metrics = sut.takeMetrics(fixture.sentryId) - - val totalFrames = metrics!![MeasurementValue.KEY_FRAMES_TOTAL] - assertEquals(totalFrames!!.value, 111) - - val frozenFrames = metrics[MeasurementValue.KEY_FRAMES_FROZEN] - assertEquals(frozenFrames!!.value, 6) - - val slowFrames = metrics[MeasurementValue.KEY_FRAMES_SLOW] - assertEquals(slowFrames!!.value, 5) - } - - @Test - fun `sets slow and frozen frames even if start was null`() { - val sut = fixture.getSut() - val array = - SparseIntArray() - .also { - it.put(16, 100) - it.put(20, 5) - it.put(701, 6) - } - .let { arrayOf(it) } - - whenever(fixture.aggregator.metrics).thenReturn(null, array) - - sut.addActivity(fixture.activity) - sut.setMetrics(fixture.activity, fixture.sentryId) - - val metrics = sut.takeMetrics(fixture.sentryId) - - val totalFrames = metrics!![MeasurementValue.KEY_FRAMES_TOTAL] - assertEquals(totalFrames!!.value, 111) - - val frozenFrames = metrics[MeasurementValue.KEY_FRAMES_FROZEN] - assertEquals(frozenFrames!!.value, 6) - - val slowFrames = metrics[MeasurementValue.KEY_FRAMES_SLOW] - assertEquals(slowFrames!!.value, 5) - } - - @Test - fun `different activities have separate counts - even when called out of order`() { - val sut = fixture.getSut() - val activityAStartFrameCounts = - SparseIntArray() - .also { - it.put(16, 100) - it.put(17, 3) - it.put(700, 2) - it.put(701, 6) - } - .let { arrayOf(it) } - val activityBStartFrameCounts = - SparseIntArray() - .also { - it.put(16, 110) - it.put(17, 3) - it.put(700, 3) - it.put(701, 7) - } - .let { arrayOf(it) } - val activityAEndFrameCounts = - SparseIntArray() - .also { - it.put(16, 115) - it.put(17, 3) - it.put(700, 5) - it.put(701, 9) - } - .let { arrayOf(it) } - val activityBEndFrameCounts = - SparseIntArray() - .also { - it.put(16, 135) - it.put(17, 3) - it.put(700, 8) - it.put(701, 12) - } - .let { arrayOf(it) } - - val activityA = fixture.activity - val activityB = mock() - val sentryIdA = fixture.sentryId - val sentryIdB = SentryId() - - whenever(fixture.aggregator.metrics) - .thenReturn( - activityAStartFrameCounts, - activityBStartFrameCounts, - activityAEndFrameCounts, - activityBEndFrameCounts, - ) - - sut.addActivity(activityA) - sut.addActivity(activityB) - sut.setMetrics(activityA, sentryIdA) - sut.setMetrics(activityB, sentryIdB) - - val metricsA = sut.takeMetrics(sentryIdA)!! - val metricsB = sut.takeMetrics(sentryIdB)!! - - val totalFramesA = metricsA!![MeasurementValue.KEY_FRAMES_TOTAL] - assertEquals(totalFramesA!!.value, 21) // 15 + 3 + 3 (diff counts for activityA) - - val frozenFramesA = metricsA[MeasurementValue.KEY_FRAMES_FROZEN] - assertEquals(frozenFramesA!!.value, 3) - - val slowFramesA = metricsA[MeasurementValue.KEY_FRAMES_SLOW] - assertEquals(slowFramesA!!.value, 3) - - val totalFramesB = metricsB!![MeasurementValue.KEY_FRAMES_TOTAL] - assertEquals(totalFramesB!!.value, 35) // 25 + 5 + 5 (diff counts for activityB) - - val frozenFramesB = metricsB[MeasurementValue.KEY_FRAMES_FROZEN] - assertEquals(frozenFramesB!!.value, 5) - - val slowFramesB = metricsB[MeasurementValue.KEY_FRAMES_SLOW] - assertEquals(slowFramesB!!.value, 5) - } - - @Test - fun `same activity can be used again later on`() { - val sut = fixture.getSut() - val firstLaunchStartFrameCounts = - SparseIntArray() - .also { - it.put(16, 100) - it.put(20, 5) - it.put(701, 6) - } - .let { arrayOf(it) } - val firstLaunchEndFrameCounts = - SparseIntArray() - .also { - it.put(16, 110) - it.put(20, 6) - it.put(701, 7) - } - .let { arrayOf(it) } - val secondLaunchStartFrameCounts = - SparseIntArray() - .also { - it.put(16, 115) - it.put(20, 8) - it.put(701, 9) - } - .let { arrayOf(it) } - val secondLaunchEndFrameCounts = - SparseIntArray() - .also { - it.put(16, 135) - it.put(20, 11) - it.put(701, 12) - } - .let { arrayOf(it) } - val secondSentryId = SentryId() - - whenever(fixture.aggregator.metrics) - .thenReturn( - firstLaunchStartFrameCounts, - firstLaunchEndFrameCounts, - secondLaunchStartFrameCounts, - secondLaunchEndFrameCounts, - ) - - sut.addActivity(fixture.activity) - sut.setMetrics(fixture.activity, fixture.sentryId) - sut.addActivity(fixture.activity) - sut.setMetrics(fixture.activity, secondSentryId) - - val metrics = sut.takeMetrics(fixture.sentryId) - val secondMetrics = sut.takeMetrics(secondSentryId) - - val totalFrames = metrics!![MeasurementValue.KEY_FRAMES_TOTAL] - assertEquals(totalFrames!!.value, 12) // 10 + 1 + 1 (diff counts for first invocation) - - val frozenFrames = metrics[MeasurementValue.KEY_FRAMES_FROZEN] - assertEquals(frozenFrames!!.value, 1) - - val slowFrames = metrics[MeasurementValue.KEY_FRAMES_SLOW] - assertEquals(slowFrames!!.value, 1) - - val totalFramesSecond = secondMetrics!![MeasurementValue.KEY_FRAMES_TOTAL] - assertEquals(totalFramesSecond!!.value, 26) // 20 + 3 + 3 (diff counts for second invocation) - - val frozenFramesSecond = secondMetrics[MeasurementValue.KEY_FRAMES_FROZEN] - assertEquals(frozenFramesSecond!!.value, 3) - - val slowFramesSecond = secondMetrics[MeasurementValue.KEY_FRAMES_SLOW] - assertEquals(slowFramesSecond!!.value, 3) - } - - @Test - fun `do not set metrics if values are zeroes`() { - val sut = fixture.getSut() - val arrayAll = SparseIntArray() - arrayAll.put(0, 0) - val array = arrayOf(arrayAll) - - whenever(fixture.aggregator.metrics).thenReturn(array) - - sut.addActivity(fixture.activity) - sut.setMetrics(fixture.activity, fixture.sentryId) - - val metrics = sut.takeMetrics(fixture.sentryId) - - assertNull(metrics) - } - - @Test - fun `do not set metrics if values are null`() { - val sut = fixture.getSut() - - whenever(fixture.aggregator.metrics).thenReturn(null) - - sut.addActivity(fixture.activity) - sut.setMetrics(fixture.activity, fixture.sentryId) - - val metrics = sut.takeMetrics(fixture.sentryId) - - assertNull(metrics) - } - - @Test - fun `addActivity does not throw if no AndroidX`() { - val sut = fixture.getSut(false) - - sut.addActivity(fixture.activity) - } - - @Test - fun `setMetrics does not throw if no AndroidX`() { - val sut = fixture.getSut(false) - - sut.setMetrics(fixture.activity, fixture.sentryId) - } - - @Test - fun `addActivity and setMetrics combined do not throw if no AndroidX`() { - val sut = fixture.getSut(false) - - sut.addActivity(fixture.activity) - sut.setMetrics(fixture.activity, fixture.sentryId) - } - - @Test - fun `setMetrics does not throw if Activity is not added`() { - whenever(fixture.aggregator.metrics).thenThrow(IllegalArgumentException()) - val sut = fixture.getSut() - - sut.setMetrics(fixture.activity, fixture.sentryId) - } - - @Test - fun `stop does not throw if no AndroidX`() { - val sut = fixture.getSut(false) - - sut.stop() - } - - @Test - fun `stop resets frameMetricsAggregator`() { - val sut = fixture.getSut() - - sut.stop() - - verify(fixture.aggregator).reset() - } - - @Test - fun `takeMetrics returns null if no AndroidX`() { - val sut = fixture.getSut(false) - - whenever(fixture.aggregator.metrics).thenReturn(emptyArray(), getArray()) - - sut.addActivity(fixture.activity) - sut.setMetrics(fixture.activity, fixture.sentryId) - - assertNull(sut.takeMetrics(fixture.sentryId)) - } - - @Test - fun `addActivity call to FrameMetricsTracker is done on the main thread, even when being called from a background thread`() { - val sut = fixture.getSut() - - val addThread = Thread { sut.addActivity(fixture.activity) } - addThread.start() - addThread.join(500) - verify(fixture.handler).post(any()) - } - - @Test - fun `setMetrics call to FrameMetricsTracker is done on the main thread, even when being called from a background thread`() { - val sut = fixture.getSut() - - val setMetricsThread = Thread { sut.setMetrics(fixture.activity, fixture.sentryId) } - setMetricsThread.start() - setMetricsThread.join(500) - verify(fixture.handler).post(any()) - } - - @Test - fun `stop call to FrameMetricsTracker is done on the main thread, even when being called from a background thread`() { - val sut = fixture.getSut() - - val stopThread = Thread { sut.stop() } - stopThread.start() - stopThread.join(500) - verify(fixture.handler).post(any()) - } - - @Test - fun `when perf-2 is enabled, activity frame metrics tracker is disabled`() { - fixture.options.isEnablePerformanceV2 = true - val sut = fixture.getSut() - assertFalse(sut.isFrameMetricsAggregatorAvailable) - } - - @Test - fun `when perf-2 is disabled, activity frame metrics tracker is enabled`() { - fixture.options.isEnablePerformanceV2 = false - val sut = fixture.getSut() - assertTrue(sut.isFrameMetricsAggregatorAvailable) - } - - private fun getArray(frameTime: Int = 1, numFrames: Int = 1): Array { - val totalArray = SparseIntArray() - totalArray.put(frameTime, numFrames) - return arrayOf(totalArray) - } -} 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 27f339aff31..ae215d29efb 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 @@ -65,7 +65,6 @@ import org.mockito.kotlin.anyOrNull import org.mockito.kotlin.argumentCaptor import org.mockito.kotlin.check import org.mockito.kotlin.clearInvocations -import org.mockito.kotlin.eq import org.mockito.kotlin.mock import org.mockito.kotlin.never import org.mockito.kotlin.times @@ -84,7 +83,6 @@ class ActivityLifecycleIntegrationTest { val scopes = mock() val options = SentryAndroidOptions().apply { dsn = "https://key@sentry.io/proj" } val bundle = mock() - val activityFramesTracker = mock() val transactionFinishedCallback = mock() lateinit var shadowActivityManager: ShadowActivityManager @@ -129,7 +127,7 @@ class ActivityLifecycleIntegrationTest { val processes = mutableListOf(process) shadowActivityManager.setProcesses(processes) - return ActivityLifecycleIntegration(application, buildInfo, activityFramesTracker) + return ActivityLifecycleIntegration(application, buildInfo) } fun createView(): View { @@ -180,16 +178,6 @@ class ActivityLifecycleIntegrationTest { verify(fixture.application).unregisterActivityLifecycleCallbacks(any()) } - @Test - fun `When ActivityLifecycleIntegration is closed, it should close the ActivityFramesTracker`() { - val sut = fixture.getSut() - sut.register(fixture.scopes, fixture.options) - - sut.close() - - verify(fixture.activityFramesTracker).stop() - } - @Test fun `When tracing is disabled, do not start tracing`() { val sut = fixture.getSut() @@ -837,18 +825,6 @@ class ActivityLifecycleIntegrationTest { ) } - @Test - fun `Activity gets added to ActivityFramesTracker during transaction creation`() { - val sut = fixture.getSut() - fixture.options.tracesSampleRate = 1.0 - sut.register(fixture.scopes, fixture.options) - - val activity = mock() - sut.onActivityStarted(activity) - - verify(fixture.activityFramesTracker).addActivity(eq(activity)) - } - @Test fun `Transaction name is the Activity's name`() { val sut = fixture.getSut() @@ -2304,19 +2280,6 @@ class ActivityLifecycleIntegrationTest { assertNotSame(propagationContextAfterNewTrace, scope.propagationContext) } - @Test - fun `when transaction is finished, sets frame metrics`() { - val sut = fixture.getSut() - fixture.options.tracesSampleRate = 1.0 - sut.register(fixture.scopes, fixture.options) - - val activity = mock() - sut.onActivityCreated(activity, fixture.bundle) - - fixture.transaction.forceFinish(OK, false, null) - verify(fixture.activityFramesTracker).setMetrics(activity, fixture.transaction.eventId) - } - @Test fun `When sentry is initialized mid activity lifecycle, last paused time should be used in favor of app start time`() { val sut = fixture.getSut(importance = RunningAppProcessInfo.IMPORTANCE_FOREGROUND) 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 caaa30152a4..b76a94f222d 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 @@ -104,7 +104,6 @@ class AndroidContinuousProfilerTest { context = ApplicationProvider.getApplicationContext() val buildInfoProvider = BuildInfoProvider(fixture.mockLogger) val loadClass = LoadClass() - val activityFramesTracker = ActivityFramesTracker(loadClass, fixture.options) AndroidOptionsInitializer.loadDefaultAndMetadataOptions( fixture.options, context, @@ -117,7 +116,6 @@ class AndroidContinuousProfilerTest { fixture.options, buildInfoProvider, loadClass, - activityFramesTracker, false, false, false, @@ -129,7 +127,6 @@ class AndroidContinuousProfilerTest { context, buildInfoProvider, loadClass, - activityFramesTracker, false, ) // Profiler doesn't start if the folder doesn't exists. diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/AndroidOptionsInitializerTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/AndroidOptionsInitializerTest.kt index b3cba238561..5b32024682a 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/AndroidOptionsInitializerTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/AndroidOptionsInitializerTest.kt @@ -99,14 +99,12 @@ class AndroidOptionsInitializerTest { ) val loadClass = LoadClass() - val activityFramesTracker = ActivityFramesTracker(loadClass, sentryOptions) AndroidOptionsInitializer.installDefaultIntegrations( if (useRealContext) context else mockContext, sentryOptions, BuildInfoProvider(AndroidLogger()), loadClass, - activityFramesTracker, false, false, false, @@ -118,7 +116,6 @@ class AndroidOptionsInitializerTest { sentryOptions, if (useRealContext) context else mockContext, loadClass, - activityFramesTracker, false, ) } @@ -138,7 +135,6 @@ class AndroidOptionsInitializerTest { sentryOptions.isDebug = true val buildInfo = createBuildInfo() val loadClass = createClassMock(classesToLoad) - val activityFramesTracker = ActivityFramesTracker(loadClass, sentryOptions) AndroidOptionsInitializer.loadDefaultAndMetadataOptions( sentryOptions, @@ -152,7 +148,6 @@ class AndroidOptionsInitializerTest { sentryOptions, buildInfo, loadClass, - activityFramesTracker, isFragmentAvailable, isTimberAvailable, isReplayAvailable, @@ -164,7 +159,6 @@ class AndroidOptionsInitializerTest { context, buildInfo, loadClass, - activityFramesTracker, isReplayAvailable, ) } @@ -801,104 +795,6 @@ class AndroidOptionsInitializerTest { assertTrue { fixture.sentryOptions.envelopeDiskCache is AndroidEnvelopeCache } } - @Test - fun `When Activity Frames Tracking is enabled, the Activity Frames Tracker should be unavailable`() { - fixture.initSut( - hasAppContext = true, - useRealContext = true, - configureOptions = { isEnableFramesTracking = true }, - ) - - val activityLifeCycleIntegration = - fixture.sentryOptions.integrations.first { it is ActivityLifecycleIntegration } - - assertFalse( - (activityLifeCycleIntegration as ActivityLifecycleIntegration) - .activityFramesTracker - .isFrameMetricsAggregatorAvailable - ) - } - - @Test - fun `When Activity Frames Tracking is enabled, the Activity Frames Tracker should be available if perfv2 is false`() { - fixture.initSut( - hasAppContext = true, - useRealContext = true, - configureOptions = { - isEnablePerformanceV2 = false - isEnableFramesTracking = true - }, - ) - - val activityLifeCycleIntegration = - fixture.sentryOptions.integrations.first { it is ActivityLifecycleIntegration } - - assertTrue( - (activityLifeCycleIntegration as ActivityLifecycleIntegration) - .activityFramesTracker - .isFrameMetricsAggregatorAvailable - ) - } - - @Test - fun `When Frames Tracking is disabled, the Activity Frames Tracker should not be available`() { - fixture.initSut( - hasAppContext = true, - useRealContext = true, - configureOptions = { isEnableFramesTracking = false }, - ) - - val activityLifeCycleIntegration = - fixture.sentryOptions.integrations.first { it is ActivityLifecycleIntegration } - - assertFalse( - (activityLifeCycleIntegration as ActivityLifecycleIntegration) - .activityFramesTracker - .isFrameMetricsAggregatorAvailable - ) - } - - @Test - fun `When Frames Tracking is initially disabled, but enabled via configureOptions it should be unavailable`() { - fixture.sentryOptions.isEnableFramesTracking = false - fixture.initSut( - hasAppContext = true, - useRealContext = true, - configureOptions = { isEnableFramesTracking = true }, - ) - - val activityLifeCycleIntegration = - fixture.sentryOptions.integrations.first { it is ActivityLifecycleIntegration } - - assertFalse( - (activityLifeCycleIntegration as ActivityLifecycleIntegration) - .activityFramesTracker - .isFrameMetricsAggregatorAvailable - ) - } - - @Test - fun `When Frames Tracking is initially disabled, but enabled via configureOptions it should be available if perfv2 is false`() { - fixture.sentryOptions.isEnableFramesTracking = false - fixture.initSut( - hasAppContext = true, - useRealContext = true, - configureOptions = { - isEnablePerformanceV2 = false - isEnableFramesTracking = true - }, - ) - - val activityLifeCycleIntegration = - fixture.sentryOptions.integrations.first { it is ActivityLifecycleIntegration } - - assertTrue( - (activityLifeCycleIntegration as ActivityLifecycleIntegration) - .activityFramesTracker - .isFrameMetricsAggregatorAvailable - ) - } - @Test fun `AssetsModulesLoader is set to options`() { fixture.initSut() @@ -966,6 +862,15 @@ class AndroidOptionsInitializerTest { assertTrue { fixture.sentryOptions.performanceCollectors.any { it is AndroidCpuCollector } } } + @Test + fun `SpanFrameMetricsCollector is set to options`() { + fixture.initSut() + + assertTrue { + fixture.sentryOptions.performanceCollectors.any { it is SpanFrameMetricsCollector } + } + } + @Test fun `DefaultCompositePerformanceCollector is set to options`() { fixture.initSut() @@ -1020,7 +925,6 @@ class AndroidOptionsInitializerTest { mockOptions, mock(), mock(), - mock(), false, false, false, @@ -1048,14 +952,12 @@ class AndroidOptionsInitializerTest { val buildInfo = mock() whenever(buildInfo.sdkInfoVersion).thenReturn(37) val loadClass = LoadClass() - val activityFramesTracker = ActivityFramesTracker(loadClass, options) AndroidOptionsInitializer.installDefaultIntegrations( fixture.context, options, buildInfo, loadClass, - activityFramesTracker, false, false, false, @@ -1073,14 +975,12 @@ class AndroidOptionsInitializerTest { val buildInfo = mock() whenever(buildInfo.sdkInfoVersion).thenReturn(36) val loadClass = LoadClass() - val activityFramesTracker = ActivityFramesTracker(loadClass, options) AndroidOptionsInitializer.installDefaultIntegrations( fixture.context, options, buildInfo, loadClass, - activityFramesTracker, false, false, false, diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/AndroidProfilerTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/AndroidProfilerTest.kt index f402af24f80..3ad5e5f3b81 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/AndroidProfilerTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/AndroidProfilerTest.kt @@ -109,7 +109,6 @@ class AndroidProfilerTest { context = ApplicationProvider.getApplicationContext() val buildInfoProvider = BuildInfoProvider(fixture.mockLogger) val loadClass = LoadClass() - val activityFramesTracker = ActivityFramesTracker(loadClass, fixture.options) AndroidOptionsInitializer.loadDefaultAndMetadataOptions( fixture.options, context, @@ -122,7 +121,6 @@ class AndroidProfilerTest { fixture.options, buildInfoProvider, loadClass, - activityFramesTracker, false, false, false, @@ -134,7 +132,6 @@ class AndroidProfilerTest { context, buildInfoProvider, loadClass, - activityFramesTracker, false, ) // Profiler doesn't start if the folder doesn't exists. 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 b37a6bbdee5..1d2b3a78685 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 @@ -124,7 +124,6 @@ class AndroidTransactionProfilerTest { context = ApplicationProvider.getApplicationContext() val buildInfoProvider = BuildInfoProvider(fixture.mockLogger) val loadClass = LoadClass() - val activityFramesTracker = ActivityFramesTracker(loadClass, fixture.options) AndroidOptionsInitializer.loadDefaultAndMetadataOptions( fixture.options, context, @@ -137,7 +136,6 @@ class AndroidTransactionProfilerTest { fixture.options, buildInfoProvider, loadClass, - activityFramesTracker, false, false, false, @@ -149,7 +147,6 @@ class AndroidTransactionProfilerTest { context, buildInfoProvider, loadClass, - activityFramesTracker, false, ) // Profiler doesn't start if the folder doesn't exists. diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/ManifestMetadataReaderTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/ManifestMetadataReaderTest.kt index 7c52f8959a7..a32abe4befd 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/ManifestMetadataReaderTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/ManifestMetadataReaderTest.kt @@ -1800,31 +1800,6 @@ class ManifestMetadataReaderTest { assertTrue(fixture.options.isSendModules) } - @Test - fun `applyMetadata reads performance-v2 flag to options`() { - // Arrange - val bundle = bundleOf(ManifestMetadataReader.ENABLE_PERFORMANCE_V2 to false) - val context = fixture.getContext(metaData = bundle) - - // Act - ManifestMetadataReader.applyMetadata(context, fixture.options, fixture.buildInfoProvider) - - // Assert - assertFalse(fixture.options.isEnablePerformanceV2) - } - - @Test - fun `applyMetadata reads performance-v2 flag to options and keeps default if not found`() { - // Arrange - val context = fixture.getContext() - - // Act - ManifestMetadataReader.applyMetadata(context, fixture.options, fixture.buildInfoProvider) - - // Assert - assertTrue(fixture.options.isEnablePerformanceV2) - } - @Test fun `applyMetadata reads standalone app start tracing flag to options`() { val bundle = bundleOf(ManifestMetadataReader.ENABLE_STANDALONE_APP_START_TRACING to true) diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/PerformanceAndroidEventProcessorTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/PerformanceAndroidEventProcessorTest.kt index 0ab922fc32a..c4f1a73c8a4 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/PerformanceAndroidEventProcessorTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/PerformanceAndroidEventProcessorTest.kt @@ -7,7 +7,6 @@ import io.sentry.Hint import io.sentry.IScopes import io.sentry.ISpan import io.sentry.ITransaction -import io.sentry.MeasurementUnit import io.sentry.SentryLongDate import io.sentry.SentryTracer import io.sentry.SpanContext @@ -38,7 +37,6 @@ import kotlin.test.assertFalse import kotlin.test.assertNull import kotlin.test.assertTrue import org.junit.runner.RunWith -import org.mockito.kotlin.any import org.mockito.kotlin.mock import org.mockito.kotlin.whenever @@ -50,18 +48,13 @@ class PerformanceAndroidEventProcessorTest { val scopes = mock() val context = TransactionContext("name", "op", TracesSamplingDecision(true)) lateinit var tracer: SentryTracer - val activityFramesTracker = mock() - fun getSut( - tracesSampleRate: Double? = 1.0, - enablePerformanceV2: Boolean = false, - ): PerformanceAndroidEventProcessor { + fun getSut(tracesSampleRate: Double? = 1.0): PerformanceAndroidEventProcessor { AppStartMetrics.getInstance().isAppLaunchedInForeground = true options.tracesSampleRate = tracesSampleRate - options.isEnablePerformanceV2 = enablePerformanceV2 whenever(scopes.options).thenReturn(options) tracer = SentryTracer(context, scopes) - return PerformanceAndroidEventProcessor(options, activityFramesTracker) + return PerformanceAndroidEventProcessor(options) } } @@ -96,7 +89,7 @@ class PerformanceAndroidEventProcessorTest { val sut = fixture.getSut() var tr = createUiLoadTransactionWithAppStartChildSpan() - setAppStart(fixture.options) + setAppStart() tr = sut.process(tr, Hint()) @@ -117,7 +110,7 @@ class PerformanceAndroidEventProcessorTest { @Test fun `standalone app start with instrumented application onCreate attaches process and application spans`() { - val sut = fixture.getSut(enablePerformanceV2 = true) + val sut = fixture.getSut() setStandaloneColdAppStartMetrics(withApplicationOnCreate = true) var tr = createStandaloneAppStartTransaction() @@ -132,7 +125,7 @@ class PerformanceAndroidEventProcessorTest { @Test fun `standalone app start without instrumented application onCreate attaches only process span`() { - val sut = fixture.getSut(enablePerformanceV2 = true) + val sut = fixture.getSut() setStandaloneColdAppStartMetrics(withApplicationOnCreate = false) var tr = createStandaloneAppStartTransaction() @@ -147,7 +140,7 @@ class PerformanceAndroidEventProcessorTest { @Test fun `standalone app start uses the transaction root span id as parent`() { - val sut = fixture.getSut(enablePerformanceV2 = true) + val sut = fixture.getSut() setStandaloneColdAppStartMetrics() var tr = createStandaloneAppStartTransaction() @@ -161,7 +154,7 @@ class PerformanceAndroidEventProcessorTest { @Test fun `standalone app start spans do not carry TTID or TTFD contributing flags`() { - val sut = fixture.getSut(enablePerformanceV2 = true) + val sut = fixture.getSut() setStandaloneColdAppStartMetrics(withApplicationOnCreate = true) var tr = createStandaloneAppStartTransaction() @@ -177,7 +170,7 @@ class PerformanceAndroidEventProcessorTest { @Test fun `foreground standalone app start sets screen and type on root and breakdown children`() { - val sut = fixture.getSut(enablePerformanceV2 = true) + val sut = fixture.getSut() AppStartMetrics.getInstance().apply { appStartType = AppStartType.COLD isAppLaunchedInForeground = true @@ -207,7 +200,7 @@ class PerformanceAndroidEventProcessorTest { @Test fun `cold standalone app start reports the same type on measurement, contexts and spans`() { - val sut = fixture.getSut(enablePerformanceV2 = true) + val sut = fixture.getSut() AppStartMetrics.getInstance().apply { appStartType = AppStartType.COLD isAppLaunchedInForeground = true @@ -239,7 +232,7 @@ class PerformanceAndroidEventProcessorTest { @Test fun `warm standalone app start reports the same type on measurement, contexts and spans`() { - val sut = fixture.getSut(enablePerformanceV2 = true) + val sut = fixture.getSut() AppStartMetrics.getInstance().apply { appStartType = AppStartType.WARM isAppLaunchedInForeground = true @@ -336,7 +329,7 @@ class PerformanceAndroidEventProcessorTest { @Test fun `headless standalone app start sets type but not screen on children`() { - val sut = fixture.getSut(enablePerformanceV2 = true) + val sut = fixture.getSut() setStandaloneColdAppStartMetrics(withApplicationOnCreate = true) var tr = createStandaloneAppStartTransaction() @@ -353,9 +346,9 @@ class PerformanceAndroidEventProcessorTest { @Test fun `ui load attached app start child does not get standalone vitals attributes`() { - setAppStart(fixture.options, coldStart = true) + setAppStart(coldStart = true) - val sut = fixture.getSut(enablePerformanceV2 = true) + val sut = fixture.getSut() var tr = createUiLoadTransactionWithAppStartChildSpan() tr = sut.process(tr, Hint()) @@ -368,13 +361,14 @@ class PerformanceAndroidEventProcessorTest { @Test fun `foreground standalone app start measurement uses foreground fallback time span`() { - val sut = fixture.getSut(enablePerformanceV2 = false) + val sut = fixture.getSut() AppStartMetrics.getInstance().apply { appStartType = AppStartType.COLD isAppLaunchedInForeground = true + // Longer than the one-minute cap, so only the foreground fallback switches to sdk init. appStartTimeSpan.apply { setStartedAt(1) - setStoppedAt(101) + setStoppedAt(TimeUnit.MINUTES.toMillis(1) + 2) } sdkInitTimeSpan.apply { setStartedAt(10) @@ -401,7 +395,7 @@ class PerformanceAndroidEventProcessorTest { @Test fun `extended app start uses the extended end for the cold start measurement`() { - val sut = fixture.getSut(enablePerformanceV2 = true) + val sut = fixture.getSut() val metrics = AppStartMetrics.getInstance() metrics.appStartType = AppStartType.COLD metrics.isAppLaunchedInForeground = true @@ -420,7 +414,7 @@ class PerformanceAndroidEventProcessorTest { @Test fun `extended app start never reports shorter than the natural first frame duration`() { - val sut = fixture.getSut(enablePerformanceV2 = true) + val sut = fixture.getSut() val metrics = AppStartMetrics.getInstance() metrics.appStartType = AppStartType.COLD metrics.isAppLaunchedInForeground = true @@ -439,7 +433,7 @@ class PerformanceAndroidEventProcessorTest { @Test fun `extended app start that hit the deadline suppresses the measurement`() { - val sut = fixture.getSut(enablePerformanceV2 = true) + val sut = fixture.getSut() val metrics = AppStartMetrics.getInstance() metrics.appStartType = AppStartType.COLD metrics.isAppLaunchedInForeground = true @@ -457,24 +451,12 @@ class PerformanceAndroidEventProcessorTest { assertFalse(tr.measurements.containsKey(MeasurementValue.KEY_APP_START_WARM)) } - @Test - fun `add cold start measurement for performance-v2`() { - val sut = fixture.getSut(enablePerformanceV2 = true) - - var tr = createUiLoadTransactionWithAppStartChildSpan() - setAppStart(fixture.options) - - tr = sut.process(tr, Hint()) - - assertTrue(tr.measurements.containsKey(MeasurementValue.KEY_APP_START_COLD)) - } - @Test fun `add warm start measurement`() { val sut = fixture.getSut() var tr = createUiLoadTransactionWithAppStartChildSpan(coldStart = false) - setAppStart(fixture.options, false) + setAppStart(false) tr = sut.process(tr, Hint()) @@ -486,7 +468,7 @@ class PerformanceAndroidEventProcessorTest { val sut = fixture.getSut() var tr = createUiLoadTransactionWithAppStartChildSpan() - setAppStart(fixture.options) + setAppStart() tr = sut.process(tr, Hint()) @@ -499,7 +481,7 @@ class PerformanceAndroidEventProcessorTest { val sut = fixture.getSut() var tr1 = createUiLoadTransactionWithAppStartChildSpan() - setAppStart(fixture.options, false) + setAppStart(false) tr1 = sut.process(tr1, Hint()) @@ -580,25 +562,6 @@ class PerformanceAndroidEventProcessorTest { assertTrue(tr.measurements.isEmpty()) } - @Test - fun `add slow and frozen frames if auto transaction`() { - val sut = fixture.getSut() - val context = TransactionContext("Activity", UI_LOAD_OP) - val tracer = SentryTracer(context, fixture.scopes) - var tr = SentryTransaction(tracer) - - val metrics = - mapOf( - MeasurementValue.KEY_FRAMES_TOTAL to - MeasurementValue(1f, MeasurementUnit.Duration.MILLISECOND.apiName()) - ) - whenever(fixture.activityFramesTracker.takeMetrics(any())).thenReturn(metrics) - - tr = sut.process(tr, Hint()) - - assertTrue(tr.measurements.containsKey(MeasurementValue.KEY_FRAMES_TOTAL)) - } - @Test fun `adds app start metrics to app start txn`() { // given some app start metrics @@ -628,7 +591,7 @@ class PerformanceAndroidEventProcessorTest { appStartMetrics.addActivityLifecycleTimeSpans(activityTimeSpan) // when an activity transaction is created - val sut = fixture.getSut(enablePerformanceV2 = true) + val sut = fixture.getSut() val context = TransactionContext("Activity", UI_LOAD_OP) val tracer = SentryTracer(context, fixture.scopes) var tr = SentryTransaction(tracer) @@ -677,7 +640,7 @@ class PerformanceAndroidEventProcessorTest { appStartMetrics.addActivityLifecycleTimeSpans(activityTimeSpan) // when an activity transaction is created - val sut = fixture.getSut(enablePerformanceV2 = true) + val sut = fixture.getSut() val context = TransactionContext("Activity", UI_LOAD_OP) val tracer = SentryTracer(context, fixture.scopes) var tr = SentryTransaction(tracer) @@ -723,7 +686,7 @@ class PerformanceAndroidEventProcessorTest { appStartMetrics.addActivityLifecycleTimeSpans(activityTimeSpan) // when an activity transaction is created - val sut = fixture.getSut(enablePerformanceV2 = true) + val sut = fixture.getSut() val context = TransactionContext("Activity", UI_LOAD_OP) val tracer = SentryTracer(context, fixture.scopes) var tr = SentryTransaction(tracer) @@ -777,7 +740,7 @@ class PerformanceAndroidEventProcessorTest { appStartMetrics.addActivityLifecycleTimeSpans(activityTimeSpan) // when an activity transaction is created - val sut = fixture.getSut(enablePerformanceV2 = true) + val sut = fixture.getSut() val context = TransactionContext("Activity", UI_LOAD_OP) val tracer = SentryTracer(context, fixture.scopes) var tr = SentryTransaction(tracer) @@ -811,7 +774,7 @@ class PerformanceAndroidEventProcessorTest { setStoppedAt(42) } // when an activity transaction is created - val sut = fixture.getSut(enablePerformanceV2 = true) + val sut = fixture.getSut() val context = TransactionContext("Activity", UI_LOAD_OP) val tracer = SentryTracer(context, fixture.scopes) var tr = SentryTransaction(tracer) @@ -836,7 +799,7 @@ class PerformanceAndroidEventProcessorTest { } // when the first activity transaction is created - val sut = fixture.getSut(enablePerformanceV2 = true) + val sut = fixture.getSut() val context = TransactionContext("Activity", UI_LOAD_OP) val tracer = SentryTracer(context, fixture.scopes) var tr = SentryTransaction(tracer) @@ -867,7 +830,7 @@ class PerformanceAndroidEventProcessorTest { appStartMetrics.appStartTimeSpan.setStoppedAt(12000) appStartMetrics.classLoadedUptimeMs = 1000 - val sut = fixture.getSut(enablePerformanceV2 = true) + val sut = fixture.getSut() val context = TransactionContext("Activity", UI_LOAD_OP) val tracer = SentryTracer(context, fixture.scopes) var tr = SentryTransaction(tracer) @@ -896,7 +859,7 @@ class PerformanceAndroidEventProcessorTest { setStoppedAt(2000) } - val sut = fixture.getSut(enablePerformanceV2 = true) + val sut = fixture.getSut() val context = TransactionContext("Activity", UI_LOAD_OP) val tracer = SentryTracer(context, fixture.scopes) var tr = SentryTransaction(tracer) @@ -927,9 +890,9 @@ class PerformanceAndroidEventProcessorTest { @Test fun `does not set start_type field for txns without app start span`() { // given some ui.load txn - setAppStart(fixture.options, coldStart = true) + setAppStart(coldStart = true) - val sut = fixture.getSut(enablePerformanceV2 = true) + val sut = fixture.getSut() val context = TransactionContext("Activity", UI_LOAD_OP) val tracer = SentryTracer(context, fixture.scopes) var tr = SentryTransaction(tracer) @@ -944,9 +907,9 @@ class PerformanceAndroidEventProcessorTest { @Test fun `sets start_type field for app context`() { // given some cold app start - setAppStart(fixture.options, coldStart = true) + setAppStart(coldStart = true) - val sut = fixture.getSut(enablePerformanceV2 = true) + val sut = fixture.getSut() val context = TransactionContext("Activity", UI_LOAD_OP) val tracer = SentryTracer(context, fixture.scopes) var tr = SentryTransaction(tracer) @@ -1231,15 +1194,14 @@ class PerformanceAndroidEventProcessorTest { assertTrue(backgroundThreadSpan.data?.get(SpanDataConvention.CONTRIBUTES_TTFD) == true) } - private fun setAppStart(options: SentryAndroidOptions, coldStart: Boolean = true) { + private fun setAppStart(coldStart: Boolean = true) { AppStartMetrics.getInstance().apply { appStartType = when (coldStart) { true -> AppStartType.COLD false -> AppStartType.WARM } - val timeSpan = if (options.isEnablePerformanceV2) appStartTimeSpan else sdkInitTimeSpan - timeSpan.apply { + appStartTimeSpan.apply { setStartedAt(1) setStoppedAt(2) } diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/SentryAndroidOptionsTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/SentryAndroidOptionsTest.kt index cc8b9100015..a68d892fe90 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/SentryAndroidOptionsTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/SentryAndroidOptionsTest.kt @@ -172,19 +172,6 @@ class SentryAndroidOptionsTest { assertFalse(options.isEnableScopeSync) } - @Test - fun `performance v2 is enabled by default`() { - val sentryOptions = SentryAndroidOptions() - assertTrue(sentryOptions.isEnablePerformanceV2) - } - - @Test - fun `performance v2 can be disabled`() { - val sentryOptions = SentryAndroidOptions() - sentryOptions.isEnablePerformanceV2 = false - assertFalse(sentryOptions.isEnablePerformanceV2) - } - @Test fun `standalone app start tracing is disabled by default`() { val sentryOptions = SentryAndroidOptions() 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 c94fd5890ca..6d02b678c5e 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 @@ -573,7 +573,6 @@ class SentryAndroidTest { fixture.initSut(context = mock()) { options -> options.dsn = "https://key@sentry.io/123" - options.isEnablePerformanceV2 = true } assertEquals(42, AppStartMetrics.getInstance().appStartTimeSpan.startUptimeMs) assertTrue(AppStartMetrics.getInstance().sdkInitTimeSpan.hasStarted()) @@ -588,7 +587,6 @@ class SentryAndroidTest { SentryShadowProcess.setStartUptimeMillis(42) fixture.initSut(context = mock()) { options -> options.dsn = "https://key@sentry.io/123" - options.isEnablePerformanceV2 = true } assertEquals(99, AppStartMetrics.getInstance().sdkInitTimeSpan.startUptimeMs) diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/SentryInitProviderTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/SentryInitProviderTest.kt index 114f6850091..26a127e4ce6 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/SentryInitProviderTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/SentryInitProviderTest.kt @@ -150,13 +150,11 @@ class SentryInitProviderTest { AndroidOptionsInitializer.loadDefaultAndMetadataOptions(sentryOptions, mockContext) val loadClass = LoadClass() - val activityFramesTracker = ActivityFramesTracker(loadClass, sentryOptions) AndroidOptionsInitializer.installDefaultIntegrations( mockContext, sentryOptions, BuildInfoProvider(AndroidLogger()), loadClass, - activityFramesTracker, false, false, false, @@ -167,7 +165,6 @@ class SentryInitProviderTest { sentryOptions, mockContext, loadClass, - activityFramesTracker, false, ) diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/SpanFrameMetricsCollectorTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/SpanFrameMetricsCollectorTest.kt index 2b6f19a8d31..be9251df1db 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/SpanFrameMetricsCollectorTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/SpanFrameMetricsCollectorTest.kt @@ -35,7 +35,6 @@ class SpanFrameMetricsCollectorTest { } options.frameMetricsCollector = frameMetricsCollector options.isEnableFramesTracking = enabled - options.isEnablePerformanceV2 = enabled options.dateProvider = SentryAndroidDateProvider() return SpanFrameMetricsCollector(options, frameMetricsCollector) diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/cache/AndroidEnvelopeCacheTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/cache/AndroidEnvelopeCacheTest.kt index 47163c0e841..6924bf23f1c 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/cache/AndroidEnvelopeCacheTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/cache/AndroidEnvelopeCacheTest.kt @@ -63,12 +63,8 @@ class AndroidEnvelopeCacheTest { if (appStartMillis != null) { AppStartMetrics.getInstance().apply { - if (options.isEnablePerformanceV2) { - appStartTimeSpan.setStartedAt(appStartMillis) - sdkInitTimeSpan.setStartedAt(appStartMillis) - } else { - sdkInitTimeSpan.setStartedAt(appStartMillis) - } + appStartTimeSpan.setStartedAt(appStartMillis) + sdkInitTimeSpan.setStartedAt(appStartMillis) } } if (currentTimeMillis != null) { diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/performance/AppStartMetricsTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/performance/AppStartMetricsTest.kt index a036583adb8..1cae2cdeba8 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/performance/AppStartMetricsTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/performance/AppStartMetricsTest.kt @@ -17,7 +17,6 @@ import io.sentry.SentryNanotimeDate import io.sentry.android.core.AppStartExtension import io.sentry.android.core.ContextUtils import io.sentry.android.core.CurrentActivityHolder -import io.sentry.android.core.SentryAndroidOptions import io.sentry.android.core.SentryShadowProcess import io.sentry.protocol.SentryId import java.util.concurrent.TimeUnit @@ -89,45 +88,24 @@ class AppStartMetricsTest { } @Test - fun `if perf-2 is enabled and app start time span is started, appStartTimeSpanWithFallback returns it`() { + fun `if app start time span is started, appStartTimeSpanWithFallback returns it`() { val appStartTimeSpan = AppStartMetrics.getInstance().appStartTimeSpan AppStartMetrics.getInstance().appStartType = AppStartMetrics.AppStartType.WARM appStartTimeSpan.setStartedAt(SystemClock.uptimeMillis()) - val options = SentryAndroidOptions().apply { isEnablePerformanceV2 = true } - - val timeSpan = AppStartMetrics.getInstance().getAppStartTimeSpanWithFallback(options) + val timeSpan = AppStartMetrics.getInstance().getAppStartTimeSpanWithFallback() assertSame(appStartTimeSpan, timeSpan) } @Test - fun `if perf-2 is disabled but app start time span has started, appStartTimeSpanWithFallback returns the sdk init span instead`() { - val appStartTimeSpan = AppStartMetrics.getInstance().appStartTimeSpan - AppStartMetrics.getInstance().appStartType = AppStartMetrics.AppStartType.COLD - AppStartMetrics.getInstance().sdkInitTimeSpan.apply { - setStartedAt(123) - setStoppedAt(456) - } - appStartTimeSpan.setStartedAt(123) - - val options = SentryAndroidOptions().apply { isEnablePerformanceV2 = false } - - val timeSpan = AppStartMetrics.getInstance().getAppStartTimeSpanWithFallback(options) - val sdkInitSpan = AppStartMetrics.getInstance().sdkInitTimeSpan - assertSame(sdkInitSpan, timeSpan) - } - - @Test - fun `if perf-2 is enabled but app start time span has not started, appStartTimeSpanWithFallback returns the sdk init span instead`() { + fun `if app start time span has not started, appStartTimeSpanWithFallback returns the sdk init span instead`() { AppStartMetrics.getInstance().appStartType = AppStartMetrics.AppStartType.COLD AppStartMetrics.getInstance().sdkInitTimeSpan.apply { setStartedAt(123) setStoppedAt(456) } - val options = SentryAndroidOptions().apply { isEnablePerformanceV2 = true } - - val timeSpan = AppStartMetrics.getInstance().getAppStartTimeSpanWithFallback(options) + val timeSpan = AppStartMetrics.getInstance().getAppStartTimeSpanWithFallback() val sdkInitSpan = AppStartMetrics.getInstance().sdkInitTimeSpan assertSame(sdkInitSpan, timeSpan) } @@ -145,12 +123,8 @@ class AppStartMetricsTest { val appStartTimeSpan = AppStartMetrics.getInstance().appStartTimeSpan appStartTimeSpan.setStartedAt(SystemClock.uptimeMillis()) assertTrue(appStartTimeSpan.hasStarted()) - AppStartMetrics.getInstance().onActivityCreated(mock(), mock()) - waitForMainLooperIdle() - - val options = SentryAndroidOptions().apply { isEnablePerformanceV2 = false } - val timeSpan = AppStartMetrics.getInstance().getAppStartTimeSpanWithFallback(options) + val timeSpan = AppStartMetrics.getInstance().getAppStartTimeSpanWithFallback() assertFalse(timeSpan.hasStarted()) } @@ -395,9 +369,7 @@ class AppStartMetricsTest { assertTrue(appStartTimeSpan.hasStarted()) AppStartMetrics.getInstance().onActivityCreated(mock(), mock()) - val options = SentryAndroidOptions().apply { isEnablePerformanceV2 = true } - - val timeSpan = AppStartMetrics.getInstance().getAppStartTimeSpanWithFallback(options) + val timeSpan = AppStartMetrics.getInstance().getAppStartTimeSpanWithFallback() assertTrue(timeSpan.hasStarted()) assertSame(appStartTimeSpan, timeSpan) } @@ -411,8 +383,7 @@ class AppStartMetricsTest { // Job on main thread checks if activity was launched waitForMainLooperIdle() - val timeSpan = - AppStartMetrics.getInstance().getAppStartTimeSpanWithFallback(SentryAndroidOptions()) + val timeSpan = AppStartMetrics.getInstance().getAppStartTimeSpanWithFallback() assertFalse(timeSpan.hasStarted()) } @@ -478,9 +449,7 @@ class AppStartMetricsTest { assertTrue(appStartTimeSpan.hasStarted()) AppStartMetrics.getInstance().onActivityCreated(mock(), mock()) - val options = SentryAndroidOptions().apply { isEnablePerformanceV2 = true } - - val timeSpan = AppStartMetrics.getInstance().getAppStartTimeSpanWithFallback(options) + val timeSpan = AppStartMetrics.getInstance().getAppStartTimeSpanWithFallback() assertFalse(timeSpan.hasStarted()) } diff --git a/sentry-android-integration-tests/sentry-uitest-android/src/androidTest/java/io/sentry/uitest/android/AutomaticSpansTest.kt b/sentry-android-integration-tests/sentry-uitest-android/src/androidTest/java/io/sentry/uitest/android/AutomaticSpansTest.kt index 49bfb3fb09f..db9a138d797 100644 --- a/sentry-android-integration-tests/sentry-uitest-android/src/androidTest/java/io/sentry/uitest/android/AutomaticSpansTest.kt +++ b/sentry-android-integration-tests/sentry-uitest-android/src/androidTest/java/io/sentry/uitest/android/AutomaticSpansTest.kt @@ -58,48 +58,6 @@ class AutomaticSpansTest : BaseUiTest() { initSentry(true) { options: SentryAndroidOptions -> options.tracesSampleRate = 1.0 options.isEnableTimeToFullDisplayTracing = true - options.isEnablePerformanceV2 = false - } - - IdlingRegistry.getInstance().register(ProfilingSampleActivity.scrollingIdlingResource) - val sampleScenario = launchActivity() - swipeList(3) - Sentry.reportFullyDisplayed() - sampleScenario.moveToState(Lifecycle.State.DESTROYED) - IdlingRegistry.getInstance().unregister(ProfilingSampleActivity.scrollingIdlingResource) - relayIdlingResource.increment() - - relay.assert { - findEnvelope { - assertEnvelopeTransaction(it.items.toList(), AndroidLogger()).transaction == - "ProfilingSampleActivity" - } - .assert { - val transactionItem: SentryTransaction = it.assertTransaction() - it.assertNoOtherItems() - val measurements = transactionItem.measurements - val frozenFrames = measurements[MeasurementValue.KEY_FRAMES_FROZEN]?.value?.toInt() ?: 0 - val slowFrames = measurements[MeasurementValue.KEY_FRAMES_SLOW]?.value?.toInt() ?: 0 - val totalFrames = measurements[MeasurementValue.KEY_FRAMES_TOTAL]?.value?.toInt() ?: 0 - assertEquals("ProfilingSampleActivity", transactionItem.transaction) - // AGP matrix tests have no frames - Assume.assumeTrue(totalFrames > 0) - assertNotEquals(totalFrames, 0) - assertTrue( - totalFrames > slowFrames + frozenFrames, - "Expected total frames ($totalFrames) to be higher than the sum of slow ($slowFrames) and frozen ($frozenFrames) frames.", - ) - } - assertNoOtherEnvelopes() - } - } - - @Test - fun checkAppStartFramesMeasurementsPerfV2() { - initSentry(true) { options: SentryAndroidOptions -> - options.tracesSampleRate = 1.0 - options.isEnableTimeToFullDisplayTracing = true - options.isEnablePerformanceV2 = true } IdlingRegistry.getInstance().register(ProfilingSampleActivity.scrollingIdlingResource) diff --git a/sentry-samples/sentry-samples-android/src/main/AndroidManifest.xml b/sentry-samples/sentry-samples-android/src/main/AndroidManifest.xml index aef1b070a7b..beb07409067 100644 --- a/sentry-samples/sentry-samples-android/src/main/AndroidManifest.xml +++ b/sentry-samples/sentry-samples-android/src/main/AndroidManifest.xml @@ -315,10 +315,6 @@ - - From 6dcaab84119e7bfaeb8d7ba5e85441cc0f0add57 Mon Sep 17 00:00:00 2001 From: Nelson Osacky Date: Wed, 4 Nov 2026 14:06:20 +0100 Subject: [PATCH 2/2] changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c0d6b1dbe1d..55f17787e22 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -52,6 +52,9 @@ - `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. - `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 + - `ActivityFramesTracker` is removed, and the `ActivityLifecycleIntegration` constructor no longer takes it ### Improvements