Skip to content
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@

### Fixes

- Native SIGSEGV errors resulting from managed NullReferenceExceptions are now suppressed on Android ([#3903](https://github.com/getsentry/sentry-dotnet/pull/3903))
- OTel activities that are marked as not recorded are no longer sent to Sentry ([#3890](https://github.com/getsentry/sentry-dotnet/pull/3890))

## 5.1.0

### Significant change in behavior

- The User.IpAddress is now only set to `{{auto}}` when `SendDefaultPii` is enabled. This change gives you control over IP address collection directly on the client ([#3893](https://github.com/getsentry/sentry-dotnet/pull/3893))

### Features
Expand Down
25 changes: 21 additions & 4 deletions src/Sentry/Platforms/Android/SentrySdk.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using Sentry.Android;
using Sentry.Android.Callbacks;
using Sentry.Android.Extensions;
using Sentry.Extensibility;
using Sentry.JavaSdk.Android.Core;

// Don't let the Sentry Android SDK auto-init, as we do that manually in SentrySdk.Init
Expand Down Expand Up @@ -99,10 +100,7 @@ private static void InitSentryAndroidSdk(SentryOptions options)
}
}

if (options.Native.EnableBeforeSend && options.BeforeSendInternal is { } beforeSend)
{
o.BeforeSend = new BeforeSendCallback(beforeSend, options, o);
}
o.BeforeSend = new BeforeSendCallback(BeforeSendWrapper(options), options, o);

// These options are from SentryAndroidOptions
o.AttachScreenshot = options.Native.AttachScreenshot;
Expand Down Expand Up @@ -172,6 +170,25 @@ private static void InitSentryAndroidSdk(SentryOptions options)
// TODO: Pause/Resume
}

internal static Func<SentryEvent, SentryHint, SentryEvent?> BeforeSendWrapper(SentryOptions options)
{
return (evt, hint) =>
{
// Suppress SIGSEGV errors.
// See: https://github.com/getsentry/sentry-dotnet/pull/3903
if (evt.SentryExceptions?.SingleOrDefault() is { Type: "SIGSEGV", Value: "Segfault" } exception)
{
options.LogDebug("Suppressing SIGSEGV (this will be thrown as a managed exception instead)");
return null;
}

// Call the user defined BeforeSend callback, if it's defined - otherwise return the event as-is
return (options.Native.EnableBeforeSend && options.BeforeSendInternal is { } beforeSend)
? beforeSend(evt, hint)
: evt;
};
}

private static void AndroidEnvironment_UnhandledExceptionRaiser(object? _, RaiseThrowableEventArgs e)
{
var description = "This exception was caught by the Android global error handler.";
Expand Down
89 changes: 89 additions & 0 deletions test/Sentry.Tests/Platforms/Android/SentrySdkTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
#if ANDROID
namespace Sentry.Tests.Platforms.Android;

public class SentrySdkTests
{
[Fact]
public void BeforeSendWrapper_SuppressSIGSEGV_ReturnsNull()
{
// Arrange
var options = new SentryOptions();
var evt = new SentryEvent
{
SentryExceptions = new[]
{
new SentryException
{
Type = "SIGSEGV",
Value = "Segfault"
}
}
};
var hint = new SentryHint();

// Act
var result = SentrySdk.BeforeSendWrapper(options).Invoke(evt, hint);

// Assert
result.Should().BeNull();
}

[Fact]
public void BeforeSendWrapper_BeforeSendCallbackDefined_CallsBeforeSend()
{
// Arrange
var beforeSend = Substitute.For<Func<SentryEvent, SentryHint, SentryEvent>>();
beforeSend.Invoke(Arg.Any<SentryEvent>(), Arg.Any<SentryHint>()).Returns(callInfo => callInfo.Arg<SentryEvent>());

var options = new SentryOptions();
options.Native.EnableBeforeSend = true;
options.SetBeforeSend(beforeSend);
var evt = new SentryEvent();
var hint = new SentryHint();

// Act
var result = SentrySdk.BeforeSendWrapper(options).Invoke(evt, hint);

// Assert
beforeSend.Received(1).Invoke(Arg.Any<SentryEvent>(), Arg.Any<SentryHint>());
result.Should().Be(evt);
}

[Fact]
public void BeforeSendWrapper_NoBeforeSendCallback_ReturnsEvent()
{
// Arrange
var options = new SentryOptions();
options.Native.EnableBeforeSend = true;
var evt = new SentryEvent();
var hint = new SentryHint();

// Act
var result = SentrySdk.BeforeSendWrapper(options).Invoke(evt, hint);

// Assert
result.Should().Be(evt);
}

[Fact]
public void BeforeSendWrapper_NativeBeforeSendDisabled_ReturnsEvent()
{
// Arrange
var beforeSend = Substitute.For<Func<SentryEvent, SentryHint, SentryEvent>>();
beforeSend.Invoke(Arg.Any<SentryEvent>(), Arg.Any<SentryHint>()).Returns(_ => null);

var options = new SentryOptions();
options.SetBeforeSend(beforeSend);
options.Native.EnableBeforeSend = false;
var evt = new SentryEvent();
var hint = new SentryHint();

// Act
var result = SentrySdk.BeforeSendWrapper(options).Invoke(evt, hint);

// Assert
beforeSend.DidNotReceive().Invoke(Arg.Any<SentryEvent>(), Arg.Any<SentryHint>());
result.Should().Be(evt);
}
}
#endif
Loading