@@ -87,7 +87,7 @@ public final class ApplicationExitInfoEventProcessor implements BackfillingEvent
8787 private final @ Nullable PersistingScopeObserver persistingScopeObserver ;
8888
8989 // Only ANRv2 events are currently enriched with hint-specific content.
90- // This can be extended to other hints like CrashNativeHint .
90+ // This can be extended to other hints like NativeCrashExit .
9191 private final @ NotNull List <HintEnricher > hintEnrichers =
9292 Collections .singletonList (new AnrHintEnricher ());
9393
@@ -142,6 +142,8 @@ public ApplicationExitInfoEventProcessor(
142142 hintEnricher .applyPreEnrichment (event , backfillable , unwrappedHint );
143143 }
144144
145+ // We always set os and device even if the ApplicationExitInfo event is not enrich-able.
146+ // The OS context may change in the meantime (OS update); we consider this an edge-case.
145147 mergeOS (event );
146148 setDevice (event );
147149
@@ -690,6 +692,7 @@ public boolean supports(@NotNull Object hint) {
690692 public void applyPreEnrichment (
691693 @ NotNull SentryEvent event , @ NotNull Backfillable hint , @ NotNull Object rawHint ) {
692694 final boolean isBackgroundAnr = isBackgroundAnr (rawHint );
695+ // we always set exception values and default platform even if the ANR is not enrich-able
693696 setDefaultPlatform (event );
694697 setAnrExceptions (event , hint , isBackgroundAnr );
695698 }
@@ -704,6 +707,9 @@ public void applyPostEnrichment(
704707
705708 private void setDefaultAnrFingerprint (
706709 final @ NotNull SentryEvent event , final boolean isBackgroundAnr ) {
710+ // sentry does not yet have a capability to provide default server-side fingerprint rules,
711+ // so we're doing this on the SDK side to group background and foreground ANRs separately
712+ // even if they have similar stacktraces.
707713 if (event .getFingerprints () == null ) {
708714 event .setFingerprints (
709715 Arrays .asList ("{{ default }}" , isBackgroundAnr ? "background-anr" : "foreground-anr" ));
@@ -717,6 +723,9 @@ private void setAppForeground(
717723 app = new App ();
718724 event .getContexts ().setApp (app );
719725 }
726+ // TODO: not entirely correct, because we define background ANRs as not the ones of
727+ // IMPORTANCE_FOREGROUND, but this doesn't mean the app was in foreground when an ANR
728+ // happened but it's our best effort for now. We could serialize AppState in theory.
720729 if (app .getInForeground () == null ) {
721730 app .setInForeground (inForeground );
722731 }
@@ -742,8 +751,11 @@ private void setAnrExceptions(
742751 if (event .getExceptions () != null ) {
743752 return ;
744753 }
754+ // AnrV2 threads contain a thread dump from the OS, so we just search for the main thread dump
755+ // and make an exception out of its stacktrace
745756 final Mechanism mechanism = new Mechanism ();
746757 if (!hint .shouldEnrich ()) {
758+ // we only enrich the latest ANR in the list, so this is historical
747759 mechanism .setType ("HistoricalAppExitInfo" );
748760 } else {
749761 mechanism .setType ("AppExitInfo" );
@@ -758,6 +770,8 @@ private void setAnrExceptions(
758770
759771 SentryThread mainThread = findMainThread (event .getThreads ());
760772 if (mainThread == null ) {
773+ // if there's no main thread in the event threads, we just create a dummy thread so the
774+ // exception is properly created as well, but without stacktrace
761775 mainThread = new SentryThread ();
762776 mainThread .setStacktrace (new SentryStackTrace ());
763777 }
0 commit comments