Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();
Expand All @@ -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) {
Expand All @@ -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);
Expand Down Expand Up @@ -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:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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()
Expand Down
Loading