From bfd99b82ac8ab205f7d6ed80b5c52878c5eff2ec Mon Sep 17 00:00:00 2001 From: Antonis Lilis Date: Fri, 31 Jul 2026 10:37:00 +0200 Subject: [PATCH 1/3] feat(android): Add NDK app hang tracking options Add enableNdkAppHangTracking and ndkAppHangTimeoutIntervalMillis options, wired through the Android bridge to SentryAndroidOptions, exposing sentry-native's heartbeat-based app-hang detection on Android. Fixes #6546 Co-Authored-By: Claude Opus 4.8 --- CHANGELOG.md | 2 + .../java/io/sentry/react/RNSentryStartTest.kt | 53 +++++++++++++++++++ .../java/io/sentry/react/RNSentryStart.java | 7 +++ packages/core/src/js/options.ts | 23 ++++++++ 4 files changed, 85 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5142eb41ba..833f762a1e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ ### Features +- Add `enableNdkAppHangTracking` and `ndkAppHangTimeoutIntervalMillis` options to enable Android NDK app hang tracking ([#6546](https://github.com/getsentry/sentry-react-native/issues/6546)) + - Add `enableMetricKit` option to enable the iOS MetricKit integration ([#6540](https://github.com/getsentry/sentry-react-native/pull/6540)) When enabled, the iOS SDK sends `MXDiskWriteExceptionDiagnostic`, `MXCPUExceptionDiagnostic` and `MXHangDiagnostic` reports to Sentry. Requires iOS 15 or later and is disabled by default. MetricKit hang diagnostics are reported by the operating system and are separate from the app hangs captured by `enableAppHangTracking`, so enabling both can result in the same hang being reported twice. diff --git a/packages/core/RNSentryAndroidTester/app/src/test/java/io/sentry/react/RNSentryStartTest.kt b/packages/core/RNSentryAndroidTester/app/src/test/java/io/sentry/react/RNSentryStartTest.kt index 1416b4de62..2b42b0b90e 100644 --- a/packages/core/RNSentryAndroidTester/app/src/test/java/io/sentry/react/RNSentryStartTest.kt +++ b/packages/core/RNSentryAndroidTester/app/src/test/java/io/sentry/react/RNSentryStartTest.kt @@ -347,6 +347,59 @@ class RNSentryStartTest { ) } + @Test + fun `when enableNdkAppHangTracking is true, NDK app hang tracking is enabled`() { + val rnOptions = JavaOnlyMap.of("enableNdkAppHangTracking", true) + val options = SentryAndroidOptions() + + RNSentryStart.getSentryAndroidOptions(options, rnOptions, logger) + + assertTrue("NDK app hang tracking should be enabled", options.isEnableNdkAppHangTracking) + } + + @Test + fun `when enableNdkAppHangTracking is false, NDK app hang tracking is disabled`() { + val rnOptions = JavaOnlyMap.of("enableNdkAppHangTracking", false) + val options = SentryAndroidOptions() + + RNSentryStart.getSentryAndroidOptions(options, rnOptions, logger) + + assertFalse("NDK app hang tracking should be disabled", options.isEnableNdkAppHangTracking) + } + + @Test + fun `when enableNdkAppHangTracking is not set, it remains at default (disabled)`() { + val rnOptions = JavaOnlyMap() + val options = SentryAndroidOptions() + + RNSentryStart.getSentryAndroidOptions(options, rnOptions, logger) + + assertFalse( + "NDK app hang tracking should be disabled by default", + options.isEnableNdkAppHangTracking, + ) + } + + @Test + fun `when ndkAppHangTimeoutIntervalMillis is set, the timeout interval is applied`() { + val rnOptions = JavaOnlyMap.of("ndkAppHangTimeoutIntervalMillis", 3000) + val options = SentryAndroidOptions() + + RNSentryStart.getSentryAndroidOptions(options, rnOptions, logger) + + assertEquals(3000L, options.ndkAppHangTimeoutIntervalMillis) + } + + @Test + fun `when ndkAppHangTimeoutIntervalMillis is not set, it remains at default (5000)`() { + val rnOptions = JavaOnlyMap() + val options = SentryAndroidOptions() + + RNSentryStart.getSentryAndroidOptions(options, rnOptions, logger) + + assertEquals(5000L, options.ndkAppHangTimeoutIntervalMillis) + } + @Test fun `network detail replay options are forwarded to the native replay options`() { val mobileReplayOptions = diff --git a/packages/core/android/src/main/java/io/sentry/react/RNSentryStart.java b/packages/core/android/src/main/java/io/sentry/react/RNSentryStart.java index 0842cb41c2..2ee396b239 100644 --- a/packages/core/android/src/main/java/io/sentry/react/RNSentryStart.java +++ b/packages/core/android/src/main/java/io/sentry/react/RNSentryStart.java @@ -202,6 +202,13 @@ static void getSentryAndroidOptions( if (rnOptions.hasKey("enableAnrFingerprinting")) { options.setEnableAnrFingerprinting(rnOptions.getBoolean("enableAnrFingerprinting")); } + if (rnOptions.hasKey("enableNdkAppHangTracking")) { + options.setEnableNdkAppHangTracking(rnOptions.getBoolean("enableNdkAppHangTracking")); + } + if (rnOptions.hasKey("ndkAppHangTimeoutIntervalMillis")) { + options.setNdkAppHangTimeoutIntervalMillis( + rnOptions.getInt("ndkAppHangTimeoutIntervalMillis")); + } if (rnOptions.hasKey("spotlight")) { if (rnOptions.getType("spotlight") == ReadableType.Boolean) { options.setEnableSpotlight(rnOptions.getBoolean("spotlight")); diff --git a/packages/core/src/js/options.ts b/packages/core/src/js/options.ts index 701296cab2..2452cdc88c 100644 --- a/packages/core/src/js/options.ts +++ b/packages/core/src/js/options.ts @@ -196,6 +196,29 @@ export interface BaseReactNativeOptions { */ appHangTimeoutInterval?: number; + /** + * When enabled, the SDK uses sentry-native's heartbeat-based app-hang detection + * to track when the application stops responding for a specific amount of time + * defined by the `ndkAppHangTimeoutIntervalMillis` option. + * + * This is independent of the JVM-based ANR detection and requires NDK to be enabled. + * + * @default false + * @platform android + */ + enableNdkAppHangTracking?: boolean; + + /** + * The minimum amount of time in milliseconds an app should be unresponsive to be + * classified as an App Hang when using NDK app-hang detection. + * + * Only has an effect if `enableNdkAppHangTracking` is `true`. + * + * @default 5000 + * @platform android + */ + ndkAppHangTimeoutIntervalMillis?: number; + /** * Use this feature to enable the Sentry MetricKit integration. * From e738571db1940d39abfc8bbcab22ff1e641086f7 Mon Sep 17 00:00:00 2001 From: Antonis Lilis Date: Fri, 31 Jul 2026 10:37:36 +0200 Subject: [PATCH 2/3] chore: point changelog entry to PR Co-Authored-By: Claude Opus 4.8 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 833f762a1e..6f4fe1fbf6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ ### Features -- Add `enableNdkAppHangTracking` and `ndkAppHangTimeoutIntervalMillis` options to enable Android NDK app hang tracking ([#6546](https://github.com/getsentry/sentry-react-native/issues/6546)) +- Add `enableNdkAppHangTracking` and `ndkAppHangTimeoutIntervalMillis` options to enable Android NDK app hang tracking ([#6548](https://github.com/getsentry/sentry-react-native/pull/6548)) - Add `enableMetricKit` option to enable the iOS MetricKit integration ([#6540](https://github.com/getsentry/sentry-react-native/pull/6540)) From 26f98fd79411142fc8210503f128323d7136c786 Mon Sep 17 00:00:00 2001 From: Antonis Lilis Date: Fri, 31 Jul 2026 11:47:03 +0200 Subject: [PATCH 3/3] Remove extra line --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b101e88ea0..7abd2a46ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,7 +17,6 @@ ### Features - Add `enableNdkAppHangTracking` and `ndkAppHangTimeoutIntervalMillis` options to enable Android NDK app hang tracking ([#6548](https://github.com/getsentry/sentry-react-native/pull/6548)) - - Add `enableMetricKit` option to enable the iOS MetricKit integration ([#6540](https://github.com/getsentry/sentry-react-native/pull/6540)) When enabled, the iOS SDK sends `MXDiskWriteExceptionDiagnostic`, `MXCPUExceptionDiagnostic` and `MXHangDiagnostic` reports to Sentry. Requires iOS 15 or later and is disabled by default. MetricKit hang diagnostics are reported by the operating system and are separate from the app hangs captured by `enableAppHangTracking`, so enabling both can result in the same hang being reported twice.