Repository navigation
Conversation
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.
|
📲 Install BuildsAndroid
|
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 9 potential issues.
❌ 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); |
There was a problem hiding this comment.
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)
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); | ||
| } | ||
| } |
There was a problem hiding this comment.
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)
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; |
There was a problem hiding this comment.
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)
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."); | ||
| } |
There was a problem hiding this comment.
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.
Reviewed by Cursor Bugbot for commit f6fcbcd. Configure here.
| } | ||
|
|
||
| //noinspection UnstableApiUsage | ||
| io.sentry.ndk.SentryNdk.init(ndkOptions); |
There was a problem hiding this comment.
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.
Reviewed by Cursor Bugbot for commit f6fcbcd. Configure here.
| bestDiff = diff; | ||
| bestMatch = exitInfo; | ||
| } | ||
| } |
There was a problem hiding this comment.
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.
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; |
There was a problem hiding this comment.
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)
Triggered by project rule: PR Review Guidelines for Cursor Bugbot
Reviewed by Cursor Bugbot for commit f6fcbcd. Configure here.


📜 Description
Adds
sentry-android-ndk-outprocas an experimental, opt-in alternative tosentry-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.
NdkIntegrationnow 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
sendDefaultPIIis enabled.🔮 Next steps
Publish
sentry-native-ndk-outprocand expose opt-in selection: