diff --git a/CHANGELOG.md b/CHANGELOG.md index c160f8e0ec..e564a70cbc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,10 @@ ## Unreleased +### Features + +- Add `enableNdkAppHangTracking` and `ndkAppHangTimeoutIntervalMillis` options to enable Android NDK app hang tracking ([#6548](https://github.com/getsentry/sentry-react-native/pull/6548)) + ### Dependencies - Bump Android SDK from v8.52.0 to v8.53.0 ([#6586](https://github.com/getsentry/sentry-react-native/pull/6586)) 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 41aa348605..3780539caf 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. *