Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ensure app context is used even when SDK is initialized via Activity Context #3669

Merged
merged 8 commits into from
Sep 26, 2024
Merged
Show file tree
Hide file tree
Changes from 7 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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
### Fixes

- Avoid stopping appStartProfiler after application creation ([#3630](https://github.com/getsentry/sentry-java/pull/3630))
- Fix ensure Application Context is used even when SDK is initialized via Activity Context ([#3669](https://github.com/getsentry/sentry-java/pull/3669))

*Breaking changes*:

Expand Down
3 changes: 2 additions & 1 deletion buildSrc/src/main/java/Config.kt
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ object Config {
val appCompat = "androidx.appcompat:appcompat:1.3.0"
val timber = "com.jakewharton.timber:timber:4.7.1"
val okhttp = "com.squareup.okhttp3:okhttp:$okHttpVersion"
val leakCanary = "com.squareup.leakcanary:leakcanary-android:2.8.1"
val leakCanary = "com.squareup.leakcanary:leakcanary-android:2.14"
val constraintLayout = "androidx.constraintlayout:constraintlayout:2.1.3"

private val lifecycleVersion = "2.2.0"
Expand Down Expand Up @@ -197,6 +197,7 @@ object Config {
val hsqldb = "org.hsqldb:hsqldb:2.6.1"
val javaFaker = "com.github.javafaker:javafaker:1.0.2"
val msgpack = "org.msgpack:msgpack-core:0.9.8"
val leakCanaryInstrumentation = "com.squareup.leakcanary:leakcanary-android-instrumentation:2.14"
}

object QualityPlugins {
Expand Down
1 change: 1 addition & 0 deletions sentry-android-core/api/sentry-android-core.api
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ public final class io/sentry/android/core/BuildInfoProvider {
}

public final class io/sentry/android/core/ContextUtils {
public static fun getApplicationContext (Landroid/content/Context;)Landroid/content/Context;
public static fun isForegroundImportance ()Z
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,7 @@ static void loadDefaultAndMetadataOptions(
final @NotNull BuildInfoProvider buildInfoProvider) {
Objects.requireNonNull(context, "The context is required.");

// it returns null if ContextImpl, so let's check for nullability
if (context.getApplicationContext() != null) {
context = context.getApplicationContext();
}
context = ContextUtils.getApplicationContext(context);

Objects.requireNonNull(options, "The options object is required.");
Objects.requireNonNull(logger, "The ILogger object is required.");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,9 @@ public AndroidTransactionProfiler(
final boolean isProfilingEnabled,
final int profilingTracesHz,
final @NotNull ISentryExecutorService executorService) {
this.context = Objects.requireNonNull(context, "The application context is required");
this.context =
Objects.requireNonNull(
ContextUtils.getApplicationContext(context), "The application context is required");
this.logger = Objects.requireNonNull(logger, "ILogger is required");
this.frameMetricsCollector =
Objects.requireNonNull(frameMetricsCollector, "SentryFrameMetricsCollector is required");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public final class AnrIntegration implements Integration, Closeable {
private final @NotNull Object startLock = new Object();

public AnrIntegration(final @NotNull Context context) {
this.context = context;
this.context = ContextUtils.getApplicationContext(context);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ public AnrV2EventProcessor(
final @NotNull SentryAndroidOptions options,
final @NotNull BuildInfoProvider buildInfoProvider,
final @Nullable SecureRandom random) {
this.context = context;
this.context = ContextUtils.getApplicationContext(context);
this.options = options;
this.buildInfoProvider = buildInfoProvider;
this.random = random;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public AnrV2Integration(final @NotNull Context context) {

AnrV2Integration(
final @NotNull Context context, final @NotNull ICurrentDateProvider dateProvider) {
this.context = context;
this.context = ContextUtils.getApplicationContext(context);
this.dateProvider = dateProvider;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ public final class AppComponentsBreadcrumbsIntegration
private @Nullable SentryAndroidOptions options;

public AppComponentsBreadcrumbsIntegration(final @NotNull Context context) {
this.context = Objects.requireNonNull(context, "Context is required");
this.context =
Objects.requireNonNull(ContextUtils.getApplicationContext(context), "Context is required");
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -383,4 +383,19 @@ static void setAppPackageInfo(
}
app.setPermissions(permissions);
}

/**
* Get the app context
*
* @return the app context, or if not available, the provided context
*/
@NotNull
public static Context getApplicationContext(final @NotNull Context context) {
// it returns null if ContextImpl, so let's check for nullability
final @Nullable Context appContext = context.getApplicationContext();
if (appContext != null) {
return appContext;
}
return context;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@ public DefaultAndroidEventProcessor(
final @NotNull Context context,
final @NotNull BuildInfoProvider buildInfoProvider,
final @NotNull SentryAndroidOptions options) {
this.context = Objects.requireNonNull(context, "The application context is required.");
this.context =
Objects.requireNonNull(
ContextUtils.getApplicationContext(context), "The application context is required.");
this.buildInfoProvider =
Objects.requireNonNull(buildInfoProvider, "The BuildInfoProvider is required.");
this.options = Objects.requireNonNull(options, "The options object is required.");
Expand All @@ -57,7 +59,7 @@ public DefaultAndroidEventProcessor(
// some device info performs disk I/O, but it's result is cached, let's pre-cache it
final @NotNull ExecutorService executorService = Executors.newSingleThreadExecutor();
this.deviceInfoUtil =
executorService.submit(() -> DeviceInfoUtil.getInstance(context, options));
executorService.submit(() -> DeviceInfoUtil.getInstance(this.context, options));
executorService.shutdown();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ public static DeviceInfoUtil getInstance(
if (instance == null) {
synchronized (DeviceInfoUtil.class) {
if (instance == null) {
instance = new DeviceInfoUtil(context.getApplicationContext(), options);
instance = new DeviceInfoUtil(ContextUtils.getApplicationContext(context), options);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ public NetworkBreadcrumbsIntegration(
final @NotNull Context context,
final @NotNull BuildInfoProvider buildInfoProvider,
final @NotNull ILogger logger) {
this.context = Objects.requireNonNull(context, "Context is required");
this.context =
Objects.requireNonNull(ContextUtils.getApplicationContext(context), "Context is required");
this.buildInfoProvider =
Objects.requireNonNull(buildInfoProvider, "BuildInfoProvider is required");
this.logger = Objects.requireNonNull(logger, "ILogger is required");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ public final class PhoneStateBreadcrumbsIntegration implements Integration, Clos
private final @NotNull Object startLock = new Object();

public PhoneStateBreadcrumbsIntegration(final @NotNull Context context) {
this.context = Objects.requireNonNull(context, "Context is required");
this.context =
Objects.requireNonNull(ContextUtils.getApplicationContext(context), "Context is required");
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,10 +159,9 @@ private void launchAppStartProfiler(final @NotNull AppStartMetrics appStartMetri

final @NotNull ITransactionProfiler appStartProfiler =
new AndroidTransactionProfiler(
context.getApplicationContext(),
context,
buildInfoProvider,
new SentryFrameMetricsCollector(
context.getApplicationContext(), logger, buildInfoProvider),
new SentryFrameMetricsCollector(context, logger, buildInfoProvider),
logger,
profilingOptions.getProfilingTracesDirPath(),
profilingOptions.isProfilingEnabled(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ public SystemEventsBreadcrumbsIntegration(final @NotNull Context context) {

public SystemEventsBreadcrumbsIntegration(
final @NotNull Context context, final @NotNull List<String> actions) {
this.context = Objects.requireNonNull(context, "Context is required");
this.context =
Objects.requireNonNull(ContextUtils.getApplicationContext(context), "Context is required");
this.actions = Objects.requireNonNull(actions, "Actions list is required");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ public final class TempSensorBreadcrumbsIntegration
private final @NotNull Object startLock = new Object();

public TempSensorBreadcrumbsIntegration(final @NotNull Context context) {
this.context = Objects.requireNonNull(context, "Context is required");
this.context =
Objects.requireNonNull(ContextUtils.getApplicationContext(context), "Context is required");
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import android.content.res.AssetManager;
import io.sentry.ILogger;
import io.sentry.SentryLevel;
import io.sentry.android.core.ContextUtils;
import io.sentry.internal.debugmeta.IDebugMetaLoader;
import java.io.BufferedInputStream;
import java.io.FileNotFoundException;
Expand All @@ -24,7 +25,7 @@ public final class AssetsDebugMetaLoader implements IDebugMetaLoader {
private final @NotNull ILogger logger;

public AssetsDebugMetaLoader(final @NotNull Context context, final @NotNull ILogger logger) {
this.context = context;
this.context = ContextUtils.getApplicationContext(context);
this.logger = logger;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import android.content.Context;
import io.sentry.ILogger;
import io.sentry.SentryLevel;
import io.sentry.android.core.ContextUtils;
import io.sentry.internal.modules.ModulesLoader;
import java.io.FileNotFoundException;
import java.io.IOException;
Expand All @@ -19,7 +20,7 @@ public final class AssetsModulesLoader extends ModulesLoader {

public AssetsModulesLoader(final @NotNull Context context, final @NotNull ILogger logger) {
super(logger);
this.context = context;
this.context = ContextUtils.getApplicationContext(context);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import io.sentry.ILogger;
import io.sentry.SentryLevel;
import io.sentry.android.core.BuildInfoProvider;
import io.sentry.android.core.ContextUtils;
import java.util.HashMap;
import java.util.Map;
import org.jetbrains.annotations.ApiStatus;
Expand All @@ -37,7 +38,7 @@ public AndroidConnectionStatusProvider(
@NotNull Context context,
@NotNull ILogger logger,
@NotNull BuildInfoProvider buildInfoProvider) {
this.context = context;
this.context = ContextUtils.getApplicationContext(context);
this.logger = logger;
this.buildInfoProvider = buildInfoProvider;
this.registeredCallbacks = new HashMap<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import io.sentry.SentryLevel;
import io.sentry.SentryOptions;
import io.sentry.android.core.BuildInfoProvider;
import io.sentry.android.core.ContextUtils;
import io.sentry.util.Objects;
import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
Expand Down Expand Up @@ -84,15 +85,17 @@ public SentryFrameMetricsCollector(
final @NotNull ILogger logger,
final @NotNull BuildInfoProvider buildInfoProvider,
final @NotNull WindowFrameMetricsManager windowFrameMetricsManager) {
Objects.requireNonNull(context, "The context is required");
final @NotNull Context appContext =
Objects.requireNonNull(
ContextUtils.getApplicationContext(context), "The context is required");
this.logger = Objects.requireNonNull(logger, "Logger is required");
this.buildInfoProvider =
Objects.requireNonNull(buildInfoProvider, "BuildInfoProvider is required");
this.windowFrameMetricsManager =
Objects.requireNonNull(windowFrameMetricsManager, "WindowFrameMetricsManager is required");

// registerActivityLifecycleCallbacks is only available if Context is an AppContext
if (!(context instanceof Application)) {
if (!(appContext instanceof Application)) {
return;
}
// FrameMetrics api is only available since sdk version N
Expand All @@ -110,7 +113,7 @@ public SentryFrameMetricsCollector(

// We have to register the lifecycle callback, even if no profile is started, otherwise when we
// start a profile, we wouldn't have the current activity and couldn't get the frameMetrics.
((Application) context).registerActivityLifecycleCallbacks(this);
((Application) appContext).registerActivityLifecycleCallbacks(this);

// Most considerations regarding timestamps of frames are inspired from JankStats library:
// https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:metrics/metrics-performance/src/main/java/androidx/metrics/performance/JankStatsApi24Impl.kt
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertNotNull
import kotlin.test.assertNull
import kotlin.test.assertSame
import kotlin.test.assertTrue

@Config(sdk = [33])
Expand Down Expand Up @@ -213,4 +214,21 @@ class ContextUtilsTest {
)
assertFalse(ContextUtils.isForegroundImportance())
}

@Test
fun `getApplicationContext returns context if app context is null`() {
val contextMock = mock<Context>()
val appContext = ContextUtils.getApplicationContext(contextMock)
assertSame(contextMock, appContext)
}

@Test
fun `getApplicationContext returns app context`() {
val contextMock = mock<Context>()
val appContextMock = mock<Context>()
whenever(contextMock.applicationContext).thenReturn(appContextMock)

val appContext = ContextUtils.getApplicationContext(contextMock)
assertSame(appContextMock, appContext)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,7 @@ class SentryAndroidTest {
optionsConfig: (SentryAndroidOptions) -> Unit = {},
callback: (session: Session?) -> Unit
) {
Mockito.mockStatic(ContextUtils::class.java).use { mockedContextUtils ->
Mockito.mockStatic(ContextUtils::class.java, Mockito.CALLS_REAL_METHODS).use { mockedContextUtils ->
mockedContextUtils.`when`<Any> { ContextUtils.isForegroundImportance() }
.thenReturn(inForeground)
SentryAndroid.init(context) { options ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ dependencies {
implementation(Config.Libs.androidxRecylerView)
implementation(Config.Libs.constraintLayout)
implementation(Config.TestLibs.espressoIdlingResource)
implementation(Config.Libs.leakCanary)

compileOnly(Config.CompileOnly.nopen)
errorprone(Config.CompileOnly.nopenChecker)
Expand All @@ -123,6 +124,7 @@ dependencies {
androidTestImplementation(Config.TestLibs.androidxTestCoreKtx)
androidTestImplementation(Config.TestLibs.mockWebserver)
androidTestImplementation(Config.TestLibs.androidxJunit)
androidTestImplementation(Config.TestLibs.leakCanaryInstrumentation)
androidTestUtil(Config.TestLibs.androidxTestOrchestrator)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ abstract class BaseUiTest {
*/
protected fun initSentry(
relayWaitForRequests: Boolean = false,
context: Context = this.context,
optionsConfiguration: ((options: SentryAndroidOptions) -> Unit)? = null
) {
relay.waitForRequests = relayWaitForRequests
Expand Down
Loading
Loading