From 18c8b26e41ec8262fca66e3296b906ba62d24301 Mon Sep 17 00:00:00 2001 From: "sentry-junior[bot]" <264270552+sentry-junior[bot]@users.noreply.github.com> Date: Tue, 18 Aug 2026 08:04:54 +0000 Subject: [PATCH 1/2] fix(android): Block profiling on unsupported system package Co-Authored-By: Markus Hintersteiner --- .../sentry/android/core/PerfettoProfiler.java | 39 ++++++++++++++++++- .../android/core/PerfettoProfilerTest.kt | 25 +++++++++++- 2 files changed, 61 insertions(+), 3 deletions(-) diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/PerfettoProfiler.java b/sentry-android-core/src/main/java/io/sentry/android/core/PerfettoProfiler.java index d09c725269..6cc9bf7286 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/PerfettoProfiler.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/PerfettoProfiler.java @@ -2,6 +2,8 @@ import android.annotation.SuppressLint; import android.content.Context; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; import android.os.Build; import android.os.Bundle; import android.os.CancellationSignal; @@ -41,9 +43,13 @@ public class PerfettoProfiler { private static final long RESULT_TIMEOUT_MS = 5000; + private static final String PROFILING_PACKAGE_NAME = "com.google.android.profiling"; + private static final long EMPTY_TRACE_PROFILING_PACKAGE_VERSION = 370546200L; + private final @NotNull ILogger logger; private final @NotNull ISentryExecutorService executorService; private final @Nullable ProfilingManager profilingManager; + private final long profilingPackageVersion; private final @NotNull CancellationSignal cancellationSignal = new CancellationSignal(); private final @NotNull Object profilingResultLock = new Object(); @@ -60,16 +66,26 @@ public PerfettoProfiler( this( logger, executorService, - (ProfilingManager) context.getSystemService(Context.PROFILING_SERVICE)); + (ProfilingManager) context.getSystemService(Context.PROFILING_SERVICE), + getProfilingPackageVersion(context, logger)); } PerfettoProfiler( final @NotNull ILogger logger, final @NotNull ISentryExecutorService executorService, final @Nullable ProfilingManager profilingManager) { + this(logger, executorService, profilingManager, 0L); + } + + PerfettoProfiler( + final @NotNull ILogger logger, + final @NotNull ISentryExecutorService executorService, + final @Nullable ProfilingManager profilingManager, + final long profilingPackageVersion) { this.logger = logger; this.executorService = executorService; this.profilingManager = profilingManager; + this.profilingPackageVersion = profilingPackageVersion; } public boolean start(final long durationMs) { @@ -84,6 +100,13 @@ public boolean start(final long durationMs) { return false; } + if (profilingPackageVersion == EMPTY_TRACE_PROFILING_PACKAGE_VERSION) { + logger.log( + SentryLevel.WARNING, + "Profiling is not supported by the installed Android profiling package version."); + return false; + } + final Bundle params = new Bundle(); params.putInt(KEY_DURATION_MS, (int) durationMs); params.putInt(KEY_FREQUENCY_HZ, PROFILING_FREQUENCY_HZ); @@ -216,6 +239,20 @@ private void deleteTraceFile(final @Nullable File traceFile) { return traceFile; } + private static long getProfilingPackageVersion( + final @NotNull Context context, final @NotNull ILogger logger) { + try { + final @NotNull PackageInfo packageInfo = + context + .getPackageManager() + .getPackageInfo(PROFILING_PACKAGE_NAME, PackageManager.MATCH_APEX); + return packageInfo.getLongVersionCode(); + } catch (PackageManager.NameNotFoundException | RuntimeException e) { + logger.log(SentryLevel.DEBUG, "Failed to resolve Android profiling package version.", e); + return 0L; + } + } + private static @NotNull String errorCodeToString(final int errorCode) { switch (errorCode) { case ProfilingResult.ERROR_FAILED_RATE_LIMIT_PROCESS: diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/PerfettoProfilerTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/PerfettoProfilerTest.kt index 0746d36dff..e11f63e79f 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/PerfettoProfilerTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/PerfettoProfilerTest.kt @@ -23,6 +23,8 @@ import org.junit.runner.RunWith import org.mockito.kotlin.any import org.mockito.kotlin.doAnswer import org.mockito.kotlin.mock +import org.mockito.kotlin.never +import org.mockito.kotlin.verify import org.mockito.kotlin.whenever import org.robolectric.annotation.Config @@ -52,8 +54,11 @@ class PerfettoProfilerTest { context = ApplicationProvider.getApplicationContext() } - private fun getSut(profilingManager: ProfilingManager? = mockProfilingManager): PerfettoProfiler { - return PerfettoProfiler(mockLogger, executor, profilingManager) + private fun getSut( + profilingManager: ProfilingManager? = mockProfilingManager, + profilingPackageVersion: Long = 0L, + ): PerfettoProfiler { + return PerfettoProfiler(mockLogger, executor, profilingManager, profilingPackageVersion) } private fun createTraceFile(): File { @@ -94,6 +99,22 @@ class PerfettoProfilerTest { assertFalse(profiler.start(60000)) } + @Test + fun `start returns false and does not request profiling for unsupported package version`() { + val profiler = getSut(profilingPackageVersion = 370546200L) + + assertFalse(profiler.start(60000)) + verify(mockProfilingManager, never()).requestProfiling(any(), any(), any(), any(), any(), any()) + } + + @Test + fun `start requests profiling for other package versions`() { + val profiler = getSut(profilingPackageVersion = 370546201L) + + assertTrue(profiler.start(60000)) + verify(mockProfilingManager).requestProfiling(any(), any(), any(), any(), any(), any()) + } + @Test fun `endAndCollect calls listener with null when never started`() { val profiler = getSut() From c404834565ade6b7c846ec59bdac262fa79b90f8 Mon Sep 17 00:00:00 2001 From: Markus Hintersteiner Date: Tue, 1 Sep 2026 16:59:37 +0200 Subject: [PATCH 2/2] fix(android): Resolve the profiling package version once and narrow its error handling --- CHANGELOG.md | 4 ++ .../sentry/android/core/PerfettoProfiler.java | 47 +++++++++++++------ 2 files changed, 36 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a45ec3fe3..cc8e5e0969 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +### Fixes + +- Skip Android profiling when the installed system profiling package delivers empty traces ([#5975](https://github.com/getsentry/sentry-java/pull/5975)) + ### Dependencies - Bump Native SDK from v0.16.2 to v0.16.3 ([#5962](https://github.com/getsentry/sentry-java/pull/5962)) diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/PerfettoProfiler.java b/sentry-android-core/src/main/java/io/sentry/android/core/PerfettoProfiler.java index 6cc9bf7286..3bcd6450f6 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/PerfettoProfiler.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/PerfettoProfiler.java @@ -13,6 +13,7 @@ import io.sentry.ILogger; import io.sentry.ISentryExecutorService; import io.sentry.SentryLevel; +import io.sentry.android.core.util.AndroidLazyEvaluator; import java.io.File; import java.util.concurrent.RejectedExecutionException; import java.util.function.Consumer; @@ -43,9 +44,25 @@ public class PerfettoProfiler { private static final long RESULT_TIMEOUT_MS = 5000; + /** Name of the APEX that provides {@link ProfilingManager}. */ private static final String PROFILING_PACKAGE_NAME = "com.google.android.profiling"; + + /** + * This version of the profiling package accepts profiling requests, but delivers empty traces. + */ private static final long EMPTY_TRACE_PROFILING_PACKAGE_VERSION = 370546200L; + /** Used when the profiling package is not installed, or its version cannot be read. */ + private static final long UNKNOWN_PROFILING_PACKAGE_VERSION = 0L; + + /** + * A new profiler is created for each profile chunk, but the profiling package cannot change while + * the process runs, as an update of it restarts the app. Thus, we read it only once, to avoid a + * binder call per chunk. + */ + private static final @NotNull AndroidLazyEvaluator profilingPackageVersionEvaluator = + new AndroidLazyEvaluator<>(PerfettoProfiler::resolveProfilingPackageVersion); + private final @NotNull ILogger logger; private final @NotNull ISentryExecutorService executorService; private final @Nullable ProfilingManager profilingManager; @@ -67,14 +84,7 @@ public PerfettoProfiler( logger, executorService, (ProfilingManager) context.getSystemService(Context.PROFILING_SERVICE), - getProfilingPackageVersion(context, logger)); - } - - PerfettoProfiler( - final @NotNull ILogger logger, - final @NotNull ISentryExecutorService executorService, - final @Nullable ProfilingManager profilingManager) { - this(logger, executorService, profilingManager, 0L); + getProfilingPackageVersion(context)); } PerfettoProfiler( @@ -103,7 +113,8 @@ public boolean start(final long durationMs) { if (profilingPackageVersion == EMPTY_TRACE_PROFILING_PACKAGE_VERSION) { logger.log( SentryLevel.WARNING, - "Profiling is not supported by the installed Android profiling package version."); + "Android profiling package version %d delivers empty traces. Profiling is disabled.", + profilingPackageVersion); return false; } @@ -239,17 +250,23 @@ private void deleteTraceFile(final @Nullable File traceFile) { return traceFile; } - private static long getProfilingPackageVersion( - final @NotNull Context context, final @NotNull ILogger logger) { + private static long getProfilingPackageVersion(final @NotNull Context context) { + final @Nullable Long version = profilingPackageVersionEvaluator.getValue(context); + return version != null ? version : UNKNOWN_PROFILING_PACKAGE_VERSION; + } + + private static @NotNull Long resolveProfilingPackageVersion(final @NotNull Context context) { try { final @NotNull PackageInfo packageInfo = context .getPackageManager() - .getPackageInfo(PROFILING_PACKAGE_NAME, PackageManager.MATCH_APEX); + .getPackageInfo( + PROFILING_PACKAGE_NAME, + PackageManager.PackageInfoFlags.of(PackageManager.MATCH_APEX)); return packageInfo.getLongVersionCode(); - } catch (PackageManager.NameNotFoundException | RuntimeException e) { - logger.log(SentryLevel.DEBUG, "Failed to resolve Android profiling package version.", e); - return 0L; + } catch (PackageManager.NameNotFoundException e) { + // The profiling package is not installed on this device + return UNKNOWN_PROFILING_PACKAGE_VERSION; } }