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.
jpnurmi
requested review from
0xadam-brown,
adinauer,
markushi,
romtsn and
runningcode
as code owners
October 21, 2026 13:08
Contributor
|
📲 Install BuildsAndroid
|
4 tasks
Replace the broad `Throwable` catches in `SentryNdkCrashService` with the exceptions the guarded code can throw, and restore the interrupt flag when the service bind is interrupted.
The service was started with `startService` but never stopped, so its process kept running after `Sentry.close()` or after the app process died. Call `stopSelf` with the start ID that was current when the worker started, so a newer start from a relaunched app is not stopped.
A new service process receives `onBind` before `onStartCommand`, so a worker started from the binder configuration could capture a stale start ID and never stop the service. Tag each start with its shared memory path and stop only when the latest start belongs to the exiting worker.
The marker was written as soon as the merged envelope existed. If the send then failed with a network error, the daemon discarded the envelope and the next launch skipped the tombstone as already reported, so the crash was lost. Write the marker only once the send succeeds.
`startService` throws when the app starts in the background, and the started service stopped about a minute after the app left the foreground, so a later crash found no daemon. Native init then fell back to fork/exec, which cannot exec `libsentry-crash.so` from inside the APK with default packaging, and stalled startup for 10 s. Bind the service with `BIND_AUTO_CREATE` and keep the binding until `SentryNdk.close()`, so the service process keeps the app's importance and is not frozen. Let crash processing continue after `onDestroy`, which runs as soon as a crashed app's binding goes away. Fail NDK init instead of falling back to fork/exec when the service is unavailable: below API 30, without a context, or when binding or configuring fails. Move the inproc `SentryNdkTest` to inproc-only test sources, since outproc init now requires a context.
The crash service picked the native crash exit info closest to the event's timestamp and ignored its pid. In a crash loop, the first poll can run before the current crash has an exit info, and the previous crash's tombstone was merged instead. Skip exit infos of other pids. The worker captures the pid when it starts, so a later configure call cannot change it.
When the app relaunched while the previous daemon was still sending its crash, or before it noticed that its process was gone, the new config was stored but no worker started, and the old worker's cleanup then cleared it. The relaunched app's next crash waited 10 s in the handler and was lost, and its descriptors leaked. Keep the per-app daemon state in a config owned by its worker, and start a worker for each config right away. The old daemon is past crash processing or only waiting for its process to go away, so the two share only process-wide native state such as the logger.
`sentry-android-ndk-outproc` depends on `io.sentry:sentry-native-ndk-outproc`, which is not on Maven Central yet. Build it from the sentry-native feature branch and publish it to Maven Local so the Build job can resolve it. TODO: Revert. This is temporary until sentry-native publishes the artifact.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 81997e9. Configure here.
The tombstone wait and the send shared `shutdownTimeoutMillis`, so a slow tombstone left the send no time at all. Wait for the app's exit info instead. The app exits right after debuggerd writes its tombstone, so the wait ends when the tombstone is there, capped at the 30 s match tolerance. Stop early when the app did not crash natively or the tombstone does not parse. Give the send the full `shutdownTimeoutMillis`, and write the marker for a merged envelope that was sent or kept for the next launch.
Name the shared-memory file after the app pid and delete leftovers whose process no longer runs when the crash service starts. A setup that fails before the daemon attaches would otherwise leave the file behind, while other processes of a multi-process app keep theirs.
With a context, NdkIntegration only looked up `SentryNdk.init(Context, SentryAndroidOptions)`. An older sentry-android-ndk without it disabled NDK. Fall back to `SentryNdk.init(SentryAndroidOptions)` and log a warning.
sentry-native-ndk-outproc no longer reads it, because it always sends native crash envelopes without a minidump. The crash service still gets the tombstone flag through its configure call.
The crash service runs in its own `.sentry_ndk_crash` process, which runs the app's `Application.onCreate`. Apps that call `SentryAndroid.init` there started a second SDK on every launch, with the tombstone, ANR and outbox senders working on the same cache directory as the app process. Return early from `SentryAndroid.init` in that process.
SentryAndroid.init now returns early in the `.sentry_ndk_crash` process, so SentryNdk.init is no longer called there.
Crash service workers for processes that crash close together can finish out of order. A worker that wrote an older tombstone timestamp let `TombstoneIntegration` report the newer crash again on the next launch. Write the marker only when it is newer than the current one, and serialize the workers.
The publish step left its 4 GB Gradle daemon running alongside the 8 GB daemon used by `make preMerge`. The job was killed about 12 minutes into the tests, most likely because the runner ran out of memory.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

📜 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: