Skip to content

Commit

Permalink
Merge e2cb750 into 503f916
Browse files Browse the repository at this point in the history
  • Loading branch information
markushi authored Oct 8, 2024
2 parents 503f916 + e2cb750 commit 4d9f0b4
Show file tree
Hide file tree
Showing 8 changed files with 106 additions and 43 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
- If you're using code obfuscation, adjust your proguard-rules accordingly, so your custom view class name is not minified
- Fix ensure Application Context is used even when SDK is initialized via Activity Context ([#3669](https://github.com/getsentry/sentry-java/pull/3669))
- Fix potential ANRs due to `Calendar.getInstance` usage in Breadcrumbs constructor ([#3736](https://github.com/getsentry/sentry-java/pull/3736))
- Fix potential ANRs due to default integrations ([#3778](https://github.com/getsentry/sentry-java/pull/3778))
- Lazily initialize heavy `SentryOptions` members to avoid ANRs on app start ([#3749](https://github.com/getsentry/sentry-java/pull/3749))

*Breaking changes*:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -382,7 +382,7 @@ public synchronized void onActivityCreated(

firstActivityCreated = true;

if (fullyDisplayedReporter != null) {
if (performanceEnabled && ttfdSpan != null && fullyDisplayedReporter != null) {
fullyDisplayedReporter.registerFullyDrawnListener(() -> onFullFrameDrawn(ttfdSpan));
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,41 +85,20 @@ public void close() throws IOException {
@SuppressWarnings("deprecation")
@Override
public void onConfigurationChanged(@NotNull Configuration newConfig) {
if (hub != null) {
final Device.DeviceOrientation deviceOrientation =
DeviceOrientations.getOrientation(context.getResources().getConfiguration().orientation);

String orientation;
if (deviceOrientation != null) {
orientation = deviceOrientation.name().toLowerCase(Locale.ROOT);
} else {
orientation = "undefined";
}

final Breadcrumb breadcrumb = new Breadcrumb();
breadcrumb.setType("navigation");
breadcrumb.setCategory("device.orientation");
breadcrumb.setData("position", orientation);
breadcrumb.setLevel(SentryLevel.INFO);

final Hint hint = new Hint();
hint.set(ANDROID_CONFIGURATION, newConfig);

hub.addBreadcrumb(breadcrumb, hint);
}
executeInBackground(() -> captureConfigurationChangedBreadcrumb(newConfig));
}

@Override
public void onLowMemory() {
createLowMemoryBreadcrumb(null);
executeInBackground(() -> captureLowMemoryBreadcrumb(null));
}

@Override
public void onTrimMemory(final int level) {
createLowMemoryBreadcrumb(level);
executeInBackground(() -> captureLowMemoryBreadcrumb(level));
}

private void createLowMemoryBreadcrumb(final @Nullable Integer level) {
private void captureLowMemoryBreadcrumb(final @Nullable Integer level) {
if (hub != null) {
final Breadcrumb breadcrumb = new Breadcrumb();
if (level != null) {
Expand Down Expand Up @@ -147,4 +126,41 @@ private void createLowMemoryBreadcrumb(final @Nullable Integer level) {
hub.addBreadcrumb(breadcrumb);
}
}

private void captureConfigurationChangedBreadcrumb(final @NotNull Configuration newConfig) {
if (hub != null) {
final Device.DeviceOrientation deviceOrientation =
DeviceOrientations.getOrientation(context.getResources().getConfiguration().orientation);

String orientation;
if (deviceOrientation != null) {
orientation = deviceOrientation.name().toLowerCase(Locale.ROOT);
} else {
orientation = "undefined";
}

final Breadcrumb breadcrumb = new Breadcrumb();
breadcrumb.setType("navigation");
breadcrumb.setCategory("device.orientation");
breadcrumb.setData("position", orientation);
breadcrumb.setLevel(SentryLevel.INFO);

final Hint hint = new Hint();
hint.set(ANDROID_CONFIGURATION, newConfig);

hub.addBreadcrumb(breadcrumb, hint);
}
}

private void executeInBackground(final @NotNull Runnable runnable) {
if (options != null) {
try {
options.getExecutorService().submit(runnable);
} catch (Throwable t) {
options
.getLogger()
.log(SentryLevel.ERROR, t, "Failed to submit app components breadcrumb task");
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ static final class SystemEventsBroadcastReceiver extends BroadcastReceiver {
private static final long DEBOUNCE_WAIT_TIME_MS = 60 * 1000;
private final @NotNull IHub hub;
private final @NotNull SentryAndroidOptions options;
private final @NotNull Debouncer debouncer =
private final @NotNull Debouncer batteryChangedDebouncer =
new Debouncer(AndroidCurrentDateProvider.getInstance(), DEBOUNCE_WAIT_TIME_MS, 0);

SystemEventsBroadcastReceiver(
Expand All @@ -221,19 +221,38 @@ static final class SystemEventsBroadcastReceiver extends BroadcastReceiver {
}

@Override
public void onReceive(Context context, Intent intent) {
final boolean shouldDebounce = debouncer.checkForDebounce();
final String action = intent.getAction();
public void onReceive(final Context context, final @NotNull Intent intent) {
final @Nullable String action = intent.getAction();
final boolean isBatteryChanged = ACTION_BATTERY_CHANGED.equals(action);
if (isBatteryChanged && shouldDebounce) {
// aligning with iOS which only captures battery status changes every minute at maximum

// aligning with iOS which only captures battery status changes every minute at maximum
if (isBatteryChanged && batteryChangedDebouncer.checkForDebounce()) {
return;
}

try {
options
.getExecutorService()
.submit(
() -> {
final Breadcrumb breadcrumb = createBreadcrumb(intent, action, isBatteryChanged);
final Hint hint = new Hint();
hint.set(ANDROID_INTENT, intent);
hub.addBreadcrumb(breadcrumb, hint);
});
} catch (Throwable t) {
options
.getLogger()
.log(SentryLevel.ERROR, t, "Failed to submit system event breadcrumb action.");
}
}

private @NotNull Breadcrumb createBreadcrumb(
final @NotNull Intent intent, final @Nullable String action, boolean isBatteryChanged) {
final Breadcrumb breadcrumb = new Breadcrumb();
breadcrumb.setType("system");
breadcrumb.setCategory("device.event");
String shortAction = StringUtils.getStringAfterDot(action);
final String shortAction = StringUtils.getStringAfterDot(action);
if (shortAction != null) {
breadcrumb.setData("action", shortAction);
}
Expand Down Expand Up @@ -273,11 +292,7 @@ public void onReceive(Context context, Intent intent) {
}
}
breadcrumb.setLevel(SentryLevel.INFO);

final Hint hint = new Hint();
hint.set(ANDROID_INTENT, intent);

hub.addBreadcrumb(breadcrumb, hint);
return breadcrumb;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,6 @@ class ActivityLifecycleIntegrationTest {
}
val bundle = mock<Bundle>()
val activityFramesTracker = mock<ActivityFramesTracker>()
val fullyDisplayedReporter = FullyDisplayedReporter.getInstance()
val transactionFinishedCallback = mock<TransactionFinishedCallback>()
lateinit var shadowActivityManager: ShadowActivityManager

Expand Down Expand Up @@ -619,11 +618,30 @@ class ActivityLifecycleIntegrationTest {
sut.onActivityCreated(activity, mock())
val ttfdSpan = sut.ttfdSpanMap[activity]
sut.ttidSpanMap.values.first().finish()
fixture.fullyDisplayedReporter.reportFullyDrawn()
fixture.options.fullyDisplayedReporter.reportFullyDrawn()
assertTrue(ttfdSpan!!.isFinished)
assertNotEquals(SpanStatus.CANCELLED, ttfdSpan.status)
}

@Test
fun `if ttfd is disabled, no listener is registered for FullyDisplayedReporter`() {
val ttfdReporter = mock<FullyDisplayedReporter>()

val sut = fixture.getSut()
fixture.options.apply {
tracesSampleRate = 1.0
isEnableTimeToFullDisplayTracing = false
fullyDisplayedReporter = ttfdReporter
}

sut.register(fixture.hub, fixture.options)

val activity = mock<Activity>()
sut.onActivityCreated(activity, mock())

verify(ttfdReporter, never()).registerFullyDrawnListener(any())
}

@Test
fun `App start is Cold when savedInstanceState is null`() {
val sut = fixture.getSut()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import io.sentry.Breadcrumb
import io.sentry.IHub
import io.sentry.SentryLevel
import io.sentry.test.ImmediateExecutorService
import org.junit.runner.RunWith
import org.mockito.kotlin.any
import org.mockito.kotlin.anyOrNull
Expand Down Expand Up @@ -88,7 +89,9 @@ class AppComponentsBreadcrumbsIntegrationTest {
@Test
fun `When low memory event, a breadcrumb with type, category and level should be set`() {
val sut = fixture.getSut()
val options = SentryAndroidOptions()
val options = SentryAndroidOptions().apply {
executorService = ImmediateExecutorService()
}
val hub = mock<IHub>()
sut.register(hub, options)
sut.onLowMemory()
Expand All @@ -104,7 +107,9 @@ class AppComponentsBreadcrumbsIntegrationTest {
@Test
fun `When trim memory event with level, a breadcrumb with type, category and level should be set`() {
val sut = fixture.getSut()
val options = SentryAndroidOptions()
val options = SentryAndroidOptions().apply {
executorService = ImmediateExecutorService()
}
val hub = mock<IHub>()
sut.register(hub, options)
sut.onTrimMemory(ComponentCallbacks2.TRIM_MEMORY_BACKGROUND)
Expand Down
1 change: 1 addition & 0 deletions sentry/api/sentry.api
Original file line number Diff line number Diff line change
Expand Up @@ -2523,6 +2523,7 @@ public class io/sentry/SentryOptions {
public fun setEnvironment (Ljava/lang/String;)V
public fun setExecutorService (Lio/sentry/ISentryExecutorService;)V
public fun setFlushTimeoutMillis (J)V
public fun setFullyDisplayedReporter (Lio/sentry/FullyDisplayedReporter;)V
public fun setGestureTargetLocators (Ljava/util/List;)V
public fun setIdleTimeout (Ljava/lang/Long;)V
public fun setIgnoredCheckIns (Ljava/util/List;)V
Expand Down
9 changes: 8 additions & 1 deletion sentry/src/main/java/io/sentry/SentryOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -431,7 +431,7 @@ public class SentryOptions {
private boolean enableTimeToFullDisplayTracing = false;

/** Screen fully displayed reporter, used for time-to-full-display spans. */
private final @NotNull FullyDisplayedReporter fullyDisplayedReporter =
private @NotNull FullyDisplayedReporter fullyDisplayedReporter =
FullyDisplayedReporter.getInstance();

private @NotNull IConnectionStatusProvider connectionStatusProvider =
Expand Down Expand Up @@ -2100,6 +2100,13 @@ public void setEnableTimeToFullDisplayTracing(final boolean enableTimeToFullDisp
return fullyDisplayedReporter;
}

@ApiStatus.Internal
@TestOnly
public void setFullyDisplayedReporter(
final @NotNull FullyDisplayedReporter fullyDisplayedReporter) {
this.fullyDisplayedReporter = fullyDisplayedReporter;
}

/**
* Whether OPTIONS requests should be traced.
*
Expand Down

0 comments on commit 4d9f0b4

Please sign in to comment.