Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## Unreleased

### Features

- On Android, iOS, and macOS, C# events now report the connection type in `device.connection_type` and the cellular network generation in `device.connection_effective_type`, for example `4g` or `5g`. On Android 11 and earlier, the generation requires the `READ_PHONE_STATE` permission. ([#2889](https://github.com/getsentry/sentry-unity/pull/2889))

## 4.11.0

### Fixes
Expand All @@ -11,6 +17,7 @@

### Features

- On Android, iOS, and macOS, C# events now report the connection type in `device.connection_type` and the cellular network generation in `device.connection_effective_type`, for example `4g` or `5g`. On Android 11 and earlier, the generation requires the `READ_PHONE_STATE` permission. ([#2889](https://github.com/getsentry/sentry-unity/pull/2889))
- Added the `NativeHandlerPath` option. It sets `handler_path` on the native SDK, so the out-of-process crash handler can be placed somewhere other than next to the player executable. Left unset, sentry-native keeps looking for the handler next to the executable as before. ([#2881](https://github.com/getsentry/sentry-unity/pull/2881))

### Dependencies
Expand Down
4 changes: 2 additions & 2 deletions global.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"sdk": {
"version": "10.0.401",
"workloadVersion": "10.0.401",
"version": "10.0.402",
"workloadVersion": "10.0.402",
"rollForward": "disable",
"allowPrerelease": false
}
Expand Down
2 changes: 2 additions & 0 deletions package-dev/Plugins/Android/proguard-sentry-unity.pro
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,6 @@
-keep class io.sentry.Scope { *; }
-keep class io.sentry.ScopeCallback { *; }
-keep class io.sentry.protocol.** { *; }
-keep class io.sentry.android.core.internal.util.AndroidConnectionStatusProvider { *; }
-keep class io.sentry.android.core.internal.util.NetworkConnection { *; }
-dontwarn io.sentry.**
13 changes: 13 additions & 0 deletions package-dev/Plugins/iOS/SentryNativeBridge.m
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,19 @@ void SentryNativeBridgeUnsetUser()
return cString;
}

static char *_Nullable _CStringCopyOrNull(id _Nullable value)
{
return [value isKindOfClass:[NSString class]] ? strdup([value UTF8String]) : NULL;
}

void SentryNativeBridgeGetConnection(
char *_Nullable *_Nonnull type, char *_Nullable *_Nonnull effectiveType)
{
NSDictionary *device = [SentryObjCSDK internal].sdk.extraContext[@"device"];
*type = _CStringCopyOrNull(device[@"connection_type"]);
*effectiveType = _CStringCopyOrNull(device[@"connection_effective_type"]);
}

void SentryNativeBridgeSetTrace(const char *traceId, const char *spanId)
{
if (traceId == NULL || spanId == NULL) {
Expand Down
6 changes: 6 additions & 0 deletions package-dev/Plugins/iOS/SentryNativeBridgeNoOp.m
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ void SentryNativeBridgeUnsetUser() { }

char *SentryNativeBridgeGetInstallationId() { return NULL; }

void SentryNativeBridgeGetConnection(char **type, char **effectiveType)
{
*type = NULL;
*effectiveType = NULL;
}

void SentryNativeBridgeSetTrace(const char *traceId, const char *spanId) { }

void SentryNativeBridgeSetEnvironment(const char *environment) { }
Expand Down
13 changes: 13 additions & 0 deletions package-dev/Plugins/macOS/SentryNativeBridge.m
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,19 @@ void SentryNativeBridgeUnsetUser()
return cString;
}

static char *_CStringCopyOrNull(id value)
{
return [value isKindOfClass:[NSString class]] ? strdup([value UTF8String]) : NULL;
}

void SentryNativeBridgeGetConnection(char **type, char **effectiveType)
{
id sdkApi = [SentryInternalApi() performSelector:@selector(sdk)];
NSDictionary *device = [sdkApi performSelector:@selector(extraContext)][@"device"];
*type = _CStringCopyOrNull(device[@"connection_type"]);
*effectiveType = _CStringCopyOrNull(device[@"connection_effective_type"]);
}

void SentryNativeBridgeSetTrace(const char *traceId, const char *spanId)
{
if (traceId == NULL || spanId == NULL) {
Expand Down
26 changes: 26 additions & 0 deletions src/Sentry.Unity.Android/SentryJava.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ internal interface ISentryJava
public void Init(SentryUnityOptions options);
public string? GetInstallationId();
public bool? CrashedLastRun();
public (string? Type, string? EffectiveType) GetConnection();
public void Close();
public void WriteScope(
string? AppStartTime,
Expand Down Expand Up @@ -231,6 +232,31 @@ public void Init(SentryUnityOptions options)
return null;
}

public (string? Type, string? EffectiveType) GetConnection()
{
if (!MainThreadData.IsMainThread())
{
_logger?.LogError("Calling GetConnection() on Android SDK requires running on MainThread");
return (null, null);
}

try
{
using var sentry = GetSentryJava();
using var hub = sentry.CallStatic<AndroidJavaObject>("getCurrentHub");
using var options = hub?.Call<AndroidJavaObject>("getOptions");
using var provider = options?.Call<AndroidJavaObject>("getConnectionStatusProvider");
using var connection = provider?.Call<AndroidJavaObject>("getConnection");
return (connection?.Get<string>("type"), connection?.Get<string>("effectiveType"));
}
catch (Exception e)
{
_logger?.LogError(e, "Calling 'SentryJava.GetConnection' failed.");
}

return (null, null);
}

public void WriteScope(
string? AppStartTime,
string? AppBuildType,
Expand Down
1 change: 1 addition & 0 deletions src/Sentry.Unity.Android/SentryNativeAndroid.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ public static void Configure(SentryUnityOptions options)
options.ScopeObserver = new AndroidJavaScopeObserver(options, SentryJava);
options.EnableScopeSync = true;
options.NativeDebugImageProvider = new Native.NativeDebugImageProvider();
options.NativeConnection = SentryJava.GetConnection;

if (options.NativeAppHangTrackingEnabled && options.NdkIntegrationEnabled)
{
Expand Down
9 changes: 9 additions & 0 deletions src/Sentry.Unity.iOS/SentryCocoaBridgeProxy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,15 @@ public static extern void AddBreadcrumb(string timestamp, string? message, strin
[DllImport("__Internal", EntryPoint = "SentryNativeBridgeGetInstallationId")]
public static extern string GetInstallationId();

[DllImport("__Internal", EntryPoint = "SentryNativeBridgeGetConnection")]
private static extern void GetConnection(out string? type, out string? effectiveType);

public static (string? Type, string? EffectiveType) GetConnection()
{
GetConnection(out var type, out var effectiveType);
return (type, effectiveType);
}

[DllImport("__Internal", EntryPoint = "SentryNativeBridgeSetTrace")]
public static extern void SetTrace(string traceId, string spanId);

Expand Down
1 change: 1 addition & 0 deletions src/Sentry.Unity.iOS/SentryNativeCocoa.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ internal static void Configure(SentryUnityOptions options, RuntimePlatform platf
SentryCocoaBridgeProxy.SetSdkName(); // Since we're not building the SDK we have to overwrite the name here

options.NativeContextWriter = new NativeContextWriter();
options.NativeConnection = SentryCocoaBridgeProxy.GetConnection;
options.EnableScopeSync = true;
options.CrashedLastRun = () =>
{
Expand Down
5 changes: 5 additions & 0 deletions src/Sentry.Unity/SentryUnityOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,11 @@ internal string? DefaultUserId
/// </summary>
internal INativeDebugImageProvider? NativeDebugImageProvider { get; set; } = null;

/// <summary>
/// Reads the connection type and the cellular network generation from the native SDK. Called on the main thread.
/// </summary>
internal Func<(string? Type, string? EffectiveType)>? NativeConnection { get; set; } = null;

/// <summary>
/// Used to close down the native SDK
/// </summary>
Expand Down
7 changes: 7 additions & 0 deletions src/Sentry.Unity/UnityEventProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,13 @@ private void PopulateDevice(Device device)
// TODO: Add to protocol?
break;
}

if (_sentryOptions.NativeConnection is { } nativeConnection)
{
var (connectionType, connectionEffectiveType) = nativeConnection();
device.ConnectionType = connectionType;
device.ConnectionEffectiveType = connectionEffectiveType;
}
}

private void PopulateUnity(Protocol.Unity unity)
Expand Down
2 changes: 1 addition & 1 deletion src/sentry-dotnet
Submodule sentry-dotnet updated 144 files
12 changes: 12 additions & 0 deletions test/Sentry.Unity.Android.Tests/SentryNativeAndroidTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,18 @@ public void Configure_NoInstallationIdReturned_DoesNotSetDefaultUserId()
Assert.IsNull(options.DefaultUserId);
}

[Test]
public void Configure_DefaultConfiguration_SetsNativeConnection()
{
var options = new SentryUnityOptions();
_testSentryJava.Connection = ("cellular", "5g");

SentryNativeAndroid.Configure(options);

Assert.IsNotNull(options.NativeConnection);
Assert.AreEqual(("cellular", "5g"), options.NativeConnection!());
}

[Test]
public void Configure_DefaultConfigurationSentryJavaNotPresent_LogsErrorAndReturns()
{
Expand Down
3 changes: 3 additions & 0 deletions test/Sentry.Unity.Android.Tests/TestSentryJava.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ internal class TestSentryJava : ISentryJava
public bool SentryPresent { get; set; } = true;
public string? InstallationId { get; set; }
public bool? IsCrashedLastRun { get; set; }
public (string? Type, string? EffectiveType) Connection { get; set; }

public bool? IsEnabled() => Enabled;

Expand All @@ -19,6 +20,8 @@ public void Init(SentryUnityOptions options) { }

public bool? CrashedLastRun() => IsCrashedLastRun;

public (string? Type, string? EffectiveType) GetConnection() => Connection;

public void Close() { }

public void WriteScope(
Expand Down
43 changes: 43 additions & 0 deletions test/Sentry.Unity.Tests/UnityEventScopeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,49 @@ public void AppMemory_SetByEventProcessor()
Assert.IsNotNull(sentryEvent.Contexts.App.Memory);
}

[Test]
public void Process_NativeConnectionSet_SetsDeviceConnection()
{
_sentryOptions.NativeConnection = () => ("cellular", "5g");
var sut = new UnityEventProcessor(_sentryOptions, new TestUnityInfo());
var sentryEvent = new SentryEvent();

sut.Process(sentryEvent);

Assert.AreEqual("cellular", sentryEvent.Contexts.Device.ConnectionType);
Assert.AreEqual("5g", sentryEvent.Contexts.Device.ConnectionEffectiveType);
}

[Test]
public void Process_NativeConnectionNotSet_LeavesDeviceConnectionEmpty()
{
var sut = new UnityEventProcessor(_sentryOptions, new TestUnityInfo());
var sentryEvent = new SentryEvent();

sut.Process(sentryEvent);

Assert.IsNull(sentryEvent.Contexts.Device.ConnectionType);
Assert.IsNull(sentryEvent.Contexts.Device.ConnectionEffectiveType);
}

[Test]
public void Process_OffMainThread_DoesNotCallNativeConnection()
{
var called = false;
_sentryOptions.NativeConnection = () =>
{
called = true;
return ("wifi", null);
};
var sut = new UnityEventProcessor(_sentryOptions, new TestUnityInfo());
var sentryEvent = new SentryEvent();

Task.Run(() => sut.Process(sentryEvent)).Wait();

Assert.IsFalse(called);
Assert.IsNull(sentryEvent.Contexts.Device.ConnectionType);
}

[Test]
public void AppMemory_SetByEventProcessorForTransactions()
{
Expand Down
1 change: 1 addition & 0 deletions test/Sentry.Unity.iOS.Tests/SentryNativeIosTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public void Configure_NativeSupportDisabled_iOS()
SentryNativeCocoa.Configure(options, RuntimePlatform.IPhonePlayer);
Assert.Null(options.ScopeObserver);
Assert.Null(options.CrashedLastRun);
Assert.Null(options.NativeConnection);
Assert.False(options.EnableScopeSync);
}

Expand Down
Loading