Skip to content

feat(android): Add experimental out-of-process crash reporting - #6347

Open
jpnurmi wants to merge 2 commits into
mainfrom
jpnurmi/feat/native-android
Open

jpnurmi wants to merge 2 commits into
mainfrom
jpnurmi/feat/native-android

Conversation

@jpnurmi

@jpnurmi jpnurmi commented Oct 21, 2026 •

Copy link
Copy Markdown
Collaborator

📜 Description

Adds sentry-android-ndk-outproc as an experimental, opt-in alternative to sentry-android-ndk. The existing inproc integration remains the default.

The new package starts the native crash daemon in a separate, non-exported Android service process on API 30+. When tombstone reporting is enabled on API 31+, the service enriches the native crash event with a matching tombstone before sending it. If enrichment is unavailable, it sends the native crash envelope without it.

NdkIntegration now passes the application context needed to configure the service, while retaining the existing initialization entry point. Both NDK variants share scope synchronization and debug-image support, and are declared mutually exclusive through Gradle capabilities.

💡 Motivation and Context

Provides the Android SDK integration for the native backend:

It allows native crash processing and tombstone enrichment to run outside the main application process. Fully enriched crash reports are delivered at the time of the crash.

💚 How did you test it?

Covered by the Android E2E tests in getsentry/sentry-native#1725, which build this Java feature branch and verify crash delivery to Sentry with tombstone enrichment enabled and disabled, without restarting the app.

📝 Checklist

  • I added GH Issue ID & Linear ID
  • I added tests to verify the changes.
  • No new PII added or SDK only sends newly added PII if sendDefaultPII is enabled.
  • I updated the docs if needed.
  • I updated the wizard if needed.
  • Review from the native team if needed.
  • No breaking change or entry added to the changelog.
  • No breaking change for hybrid SDKs or communicated to hybrid SDKs.
  • Public API changes reviewed by another Mobile SDK team member or implemented according to the develop docs spec.

🔮 Next steps

Publish sentry-native-ndk-outproc and expose opt-in selection:

Run the NDK crash daemon from an isolated Android service process so it can
release the crashing app, merge the resulting tombstone, and send the
enriched envelope before shutdown.
@github-actions

github-actions Bot commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor
Messages
📖 Do not forget to update Sentry-docs with your feature once the pull request gets approved.

Generated by 🚫 dangerJS against 222a934

@sentry

sentry Bot commented Oct 21, 2026 •

Copy link
Copy Markdown

📲 Install Builds

Android

🔗 App Name App ID Version Configuration
SDK Size io.sentry.tests.size 8.61.0 (1) release

⚙️ sentry-android Build Distribution Settings

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 9 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit f6fcbcd. Configure here.

new ArrayList<>(options.getInAppExcludes()),
notifyRead,
readyWrite,
messageRead);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Init blocks main thread for bind

High Severity

SentryNdkCrashService.start runs during Sentry.init on the main thread and waits up to 10 seconds for bindService plus a Binder configure call. That also cold-starts a new process and creates directories, which can ANR app startup.

Additional Locations (2)
Fix in Cursor Fix in Web

Triggered by project rule: PR Review Guidelines for Cursor Bugbot

Reviewed by Cursor Bugbot for commit f6fcbcd. Configure here.

throw new IllegalStateException(
"Thread interrupted while waiting for NDK libs to be loaded", e);
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Close leaves crash service running

Medium Severity

SentryNdkCrashService is started during NDK init, but SentryNdk.close only closes the native SDK and never stops or unbinds the service. The extra process and daemon keep running after Sentry.close(), and a later re-init can attach new FDs to a worker still blocked on the old ones.

Additional Locations (1)
Fix in Cursor Fix in Web

Triggered by project rule: PR Review Guidelines for Cursor Bugbot

Reviewed by Cursor Bugbot for commit f6fcbcd. Configure here.

TombstoneIntegration.TombstonePolicy.mergeNativeCrashes(nativeEnvelope.event, tombstoneEvent);
final File mergedEnvelope = writeMergedEnvelope(options, file, nativeEnvelope);
writeLastTombstoneMarker(file, exitInfo.getTimestamp());
return mergedEnvelope;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tombstone marker written before send

High Severity

writeLastTombstoneMarker runs as soon as a merged envelope is written, before daemon.sendEnvelope. If the send fails, TombstoneIntegration treats the crash as already reported and the event is lost.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit f6fcbcd. Configure here.

mergedEnvelope != null ? mergedEnvelope.getPath() : envelope.getPath(),
remainingMillis)) {
options.getLogger().log(SentryLevel.DEBUG, "Failed to send NDK crash envelope.");
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unenriched send skips tombstone marker

Medium Severity

When tombstone enrichment times out, the service still sends the native envelope but never writes last_tombstone_report. The next app start can emit a second event for the same crash through TombstoneIntegration.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit f6fcbcd. Configure here.

}

//noinspection UnstableApiUsage
io.sentry.ndk.SentryNdk.init(ndkOptions);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Failed service start is ignored

Medium Severity

SentryNdkCrashService.start can fail to create the database dir, bind, or configure, but SentryNdk.init ignores the return value and still calls io.sentry.ndk.SentryNdk.init. The out-of-process backend then has no daemon and native crashes are not processed.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit f6fcbcd. Configure here.

bestDiff = diff;
bestMatch = exitInfo;
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tombstone match ignores stored PID

Medium Severity

findMatchingExitInfo walks historical native crashes and picks the closest timestamp within 30 seconds, but never compares exitInfo.getPid() to the stored appPid. A recent crash from another process or a crash loop can merge the wrong tombstone.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit f6fcbcd. Configure here.

return true;
} catch (Throwable e) {
options.getLogger().log(SentryLevel.DEBUG, e, "Failed to start NDK crash service.");
return false;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New broad Throwable catches added

Medium Severity

New catch (Throwable) blocks in the crash service swallow fatal errors such as OutOfMemoryError and ThreadDeath without ExceptionUtils.rethrowIfFatal. The same pattern also appears in processEnvelope and readNativeEnvelope.

Additional Locations (2)
Fix in Cursor Fix in Web

Triggered by project rule: PR Review Guidelines for Cursor Bugbot

Reviewed by Cursor Bugbot for commit f6fcbcd. Configure here.

Comment thread settings.gradle.kts

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant