From d7e5583891004da53e399c3264a0ca835a23253f Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Tue, 14 Jun 2022 11:16:41 -0700 Subject: [PATCH 1/9] Copy device context from Android SDK --- src/Sentry/Android/AndroidEventProcessor.cs | 38 +++++++++++++ .../Android/Extensions/DeviceExtensions.cs | 56 +++++++++++++++++++ src/Sentry/Android/SentrySdk.cs | 37 +++++------- 3 files changed, 108 insertions(+), 23 deletions(-) create mode 100644 src/Sentry/Android/AndroidEventProcessor.cs create mode 100644 src/Sentry/Android/Extensions/DeviceExtensions.cs diff --git a/src/Sentry/Android/AndroidEventProcessor.cs b/src/Sentry/Android/AndroidEventProcessor.cs new file mode 100644 index 0000000000..5358b3a056 --- /dev/null +++ b/src/Sentry/Android/AndroidEventProcessor.cs @@ -0,0 +1,38 @@ +using Sentry.Android.Extensions; +using Sentry.Extensibility; +using Sentry.Protocol; + +namespace Sentry.Android; + +internal class AndroidEventProcessor : ISentryEventProcessor, IDisposable +{ + private readonly Java.IEventProcessor _androidProcessor; + private readonly Java.Hint _hint = new(); + + public AndroidEventProcessor(SentryAndroidOptions androidOptions) + { + _androidProcessor = androidOptions.EventProcessors.OfType() + .Where(x => x.Class.Name == "io.sentry.android.core.DefaultAndroidEventProcessor") + .Cast() + .First(); + } + + public SentryEvent Process(SentryEvent @event) + { + // Run a fake event through the Android processor, so we can get context info from the Android SDK. + // We'll want to do this every time, so that all information is current. (ex: device orientation) + using var e = new Java.SentryEvent(); + _androidProcessor.Process(e, _hint); + + // Copy what we need to the managed event + e.Contexts.Device?.ApplyTo(@event.Contexts.Device); + + return @event; + } + + public void Dispose() + { + _androidProcessor.Dispose(); + _hint.Dispose(); + } +} diff --git a/src/Sentry/Android/Extensions/DeviceExtensions.cs b/src/Sentry/Android/Extensions/DeviceExtensions.cs new file mode 100644 index 0000000000..c3e99f3b87 --- /dev/null +++ b/src/Sentry/Android/Extensions/DeviceExtensions.cs @@ -0,0 +1,56 @@ +using Sentry.Protocol; + +namespace Sentry.Android.Extensions; + +internal static class DeviceExtensions +{ + public static void ApplyTo(this Java.Protocol.Device d, Device device) + { + device.Name = d.Name; + device.Manufacturer = d.Manufacturer; + device.Brand = d.Brand; + device.Family = d.Family; + device.Model = d.Model; + device.ModelId = d.ModelId; + device.Architecture = d.GetArchs()?.FirstOrDefault(); + device.BatteryLevel = d.BatteryLevel?.ShortValue(); + device.IsCharging = d.IsCharging()?.BooleanValue(); + device.IsOnline = d.IsOnline()?.BooleanValue(); + device.Orientation = d.Orientation?.ToDeviceOrientation(); + device.Simulator = d.IsSimulator()?.BooleanValue(); + device.MemorySize = d.MemorySize?.LongValue(); + device.FreeMemory = d.FreeMemory?.LongValue(); + device.UsableMemory = d.UsableMemory?.LongValue(); + device.LowMemory = d.IsLowMemory()?.BooleanValue(); + device.StorageSize = d.StorageSize?.LongValue(); + device.FreeStorage = d.FreeStorage?.LongValue(); + device.ExternalStorageSize = d.ExternalStorageSize?.LongValue(); + device.ExternalFreeStorage = d.ExternalFreeStorage?.LongValue(); + device.ScreenResolution = $"{d.ScreenWidthPixels}x{d.ScreenHeightPixels}"; + device.ScreenDensity = d.ScreenDensity?.FloatValue(); + device.ScreenDpi = d.ScreenDpi?.IntValue(); + device.BootTime = d.BootTime?.ToDateTimeOffset(); + device.DeviceUniqueIdentifier = d.Id; + + // TODO: Can we get these from somewhere? + //device.ProcessorCount = ? + //device.CpuDescription = ? + //device.ProcessorFrequency = ? + //device.DeviceType = ? + //device.BatteryStatus = ? + //device.SupportsVibration = ? + //device.SupportsAccelerometer = ? + //device.SupportsGyroscope = ? + //device.SupportsAudio = ? + //device.SupportsLocationService = ? + + } + + public static DeviceOrientation ToDeviceOrientation(this Java.Protocol.Device.DeviceOrientation orientation) => + orientation.Name() switch + { + "PORTRAIT" => DeviceOrientation.Portrait, + "LANDSCAPE" => DeviceOrientation.Landscape, + _ => throw new ArgumentOutOfRangeException(nameof(orientation), orientation.Name(), message: default) + }; +} diff --git a/src/Sentry/Android/SentrySdk.cs b/src/Sentry/Android/SentrySdk.cs index b34f2e2526..519de58b4f 100644 --- a/src/Sentry/Android/SentrySdk.cs +++ b/src/Sentry/Android/SentrySdk.cs @@ -29,30 +29,14 @@ public static IDisposable Init(AndroidContext context, Action? co /// An object that should be disposed when the application terminates. public static IDisposable Init(AndroidContext context, SentryOptions options) { - // TODO: Pause/Resume - options.AutoSessionTracking = true; - options.IsGlobalModeEnabled = true; - options.AddEventProcessor(new DelegateEventProcessor(evt => - { - if (AndroidBuild.SupportedAbis is { } abis) - { - evt.Contexts.Device.Architecture = abis[0]; - } - else - { -#pragma warning disable CS0618 // Type or member is obsolete - evt.Contexts.Device.Architecture = AndroidBuild.CpuAbi; -#pragma warning restore CS0618 // Type or member is obsolete - } - - evt.Contexts.Device.Manufacturer = AndroidBuild.Manufacturer; - - return evt; - })); - + // Init the Java Android SDK first + SentryAndroidOptions? androidOptions = null; SentryAndroid.Init(context, new JavaLogger(options), new OptionsConfigurationCallback(o => { + // Capture the android options reference on the outer scope + androidOptions = o; + // TODO: Should we set the DistinctId to match the one used by GlobalSessionManager? //o.DistinctId = ? @@ -168,10 +152,17 @@ public static IDisposable Init(AndroidContext context, SentryOptions options) o.AddIgnoredExceptionForType(JavaClass.ForName("android.runtime.JavaProxyThrowable")); })); - options.CrashedLastRun = () => Java.Sentry.IsCrashedLastRun()?.BooleanValue() is true; - + // Make sure we capture managed exceptions from the Android environment AndroidEnvironment.UnhandledExceptionRaiser += AndroidEnvironment_UnhandledExceptionRaiser; + // Set options for the managed SDK + options.AutoSessionTracking = true; + options.IsGlobalModeEnabled = true; + options.AddEventProcessor(new AndroidEventProcessor(androidOptions!)); + options.CrashedLastRun = () => Java.Sentry.IsCrashedLastRun()?.BooleanValue() is true; + // TODO: Pause/Resume + + // Init the managed SDK return Init(options); } From 9f9dc465b07200ef98c4f0d7455517cc11e857bc Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Tue, 14 Jun 2022 11:54:16 -0700 Subject: [PATCH 2/9] Update comment --- src/Sentry/Protocol/Device.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Sentry/Protocol/Device.cs b/src/Sentry/Protocol/Device.cs index b784cab7ae..0ecd80d3b7 100644 --- a/src/Sentry/Protocol/Device.cs +++ b/src/Sentry/Protocol/Device.cs @@ -209,10 +209,12 @@ public sealed class Device : IJsonSerializable /// /// /// iOS: UIDevice.identifierForVendor (UUID) - /// Android: md5 of ANDROID_ID + /// Android: The generated Installation ID /// Windows Store Apps: AdvertisingManager::AdvertisingId (possible fallback to HardwareIdentification::GetPackageSpecificToken().Id) /// Windows Standalone: hash from the concatenation of strings taken from Computer System Hardware Classes /// + /// TODO: Investigate - Do ALL platforms now return a generated installation ID? + /// See https://github.com/getsentry/sentry-java/pull/1455 public string? DeviceUniqueIdentifier { get; set; } /// From fb49fe365ae55f4398fc6c9cc0762251bc96d5a7 Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Tue, 14 Jun 2022 12:52:24 -0700 Subject: [PATCH 3/9] Update DeviceExtensions.cs --- .../Android/Extensions/DeviceExtensions.cs | 70 +++++++++---------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/src/Sentry/Android/Extensions/DeviceExtensions.cs b/src/Sentry/Android/Extensions/DeviceExtensions.cs index c3e99f3b87..ab5f2b3034 100644 --- a/src/Sentry/Android/Extensions/DeviceExtensions.cs +++ b/src/Sentry/Android/Extensions/DeviceExtensions.cs @@ -6,43 +6,43 @@ internal static class DeviceExtensions { public static void ApplyTo(this Java.Protocol.Device d, Device device) { - device.Name = d.Name; - device.Manufacturer = d.Manufacturer; - device.Brand = d.Brand; - device.Family = d.Family; - device.Model = d.Model; - device.ModelId = d.ModelId; - device.Architecture = d.GetArchs()?.FirstOrDefault(); - device.BatteryLevel = d.BatteryLevel?.ShortValue(); - device.IsCharging = d.IsCharging()?.BooleanValue(); - device.IsOnline = d.IsOnline()?.BooleanValue(); - device.Orientation = d.Orientation?.ToDeviceOrientation(); - device.Simulator = d.IsSimulator()?.BooleanValue(); - device.MemorySize = d.MemorySize?.LongValue(); - device.FreeMemory = d.FreeMemory?.LongValue(); - device.UsableMemory = d.UsableMemory?.LongValue(); - device.LowMemory = d.IsLowMemory()?.BooleanValue(); - device.StorageSize = d.StorageSize?.LongValue(); - device.FreeStorage = d.FreeStorage?.LongValue(); - device.ExternalStorageSize = d.ExternalStorageSize?.LongValue(); - device.ExternalFreeStorage = d.ExternalFreeStorage?.LongValue(); - device.ScreenResolution = $"{d.ScreenWidthPixels}x{d.ScreenHeightPixels}"; - device.ScreenDensity = d.ScreenDensity?.FloatValue(); - device.ScreenDpi = d.ScreenDpi?.IntValue(); - device.BootTime = d.BootTime?.ToDateTimeOffset(); - device.DeviceUniqueIdentifier = d.Id; + device.Name ??= d.Name; + device.Manufacturer ??= d.Manufacturer; + device.Brand ??= d.Brand; + device.Family ??= d.Family; + device.Model ??= d.Model; + device.ModelId ??= d.ModelId; + device.Architecture ??= d.GetArchs()?.FirstOrDefault(); + device.BatteryLevel ??= d.BatteryLevel?.ShortValue(); + device.IsCharging ??= d.IsCharging()?.BooleanValue(); + device.IsOnline ??= d.IsOnline()?.BooleanValue(); + device.Orientation ??= d.Orientation?.ToDeviceOrientation(); + device.Simulator ??= d.IsSimulator()?.BooleanValue(); + device.MemorySize ??= d.MemorySize?.LongValue(); + device.FreeMemory ??= d.FreeMemory?.LongValue(); + device.UsableMemory ??= d.UsableMemory?.LongValue(); + device.LowMemory ??= d.IsLowMemory()?.BooleanValue(); + device.StorageSize ??= d.StorageSize?.LongValue(); + device.FreeStorage ??= d.FreeStorage?.LongValue(); + device.ExternalStorageSize ??= d.ExternalStorageSize?.LongValue(); + device.ExternalFreeStorage ??= d.ExternalFreeStorage?.LongValue(); + device.ScreenResolution ??= $"{d.ScreenWidthPixels}x{d.ScreenHeightPixels}"; + device.ScreenDensity ??= d.ScreenDensity?.FloatValue(); + device.ScreenDpi ??= d.ScreenDpi?.IntValue(); + device.BootTime ??= d.BootTime?.ToDateTimeOffset(); + device.DeviceUniqueIdentifier ??= d.Id; // TODO: Can we get these from somewhere? - //device.ProcessorCount = ? - //device.CpuDescription = ? - //device.ProcessorFrequency = ? - //device.DeviceType = ? - //device.BatteryStatus = ? - //device.SupportsVibration = ? - //device.SupportsAccelerometer = ? - //device.SupportsGyroscope = ? - //device.SupportsAudio = ? - //device.SupportsLocationService = ? + //device.ProcessorCount ??= ? + //device.CpuDescription ??= ? + //device.ProcessorFrequency ??= ? + //device.DeviceType ??= ? + //device.BatteryStatus ??= ? + //device.SupportsVibration ??= ? + //device.SupportsAccelerometer ??= ? + //device.SupportsGyroscope ??= ? + //device.SupportsAudio ??= ? + //device.SupportsLocationService ??= ? } From 6cbdfc1da216abe6caa9a5e82c061ad0008547d3 Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Tue, 14 Jun 2022 13:18:00 -0700 Subject: [PATCH 4/9] Add device data from MAUI --- src/Sentry.Maui/Internal/MauiDeviceData.cs | 90 +++++++++++++++++++ .../Internal/SentryMauiEventProcessor.cs | 2 +- 2 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 src/Sentry.Maui/Internal/MauiDeviceData.cs diff --git a/src/Sentry.Maui/Internal/MauiDeviceData.cs b/src/Sentry.Maui/Internal/MauiDeviceData.cs new file mode 100644 index 0000000000..87dc6e6cc1 --- /dev/null +++ b/src/Sentry.Maui/Internal/MauiDeviceData.cs @@ -0,0 +1,90 @@ +using Sentry.Protocol; +using Device = Sentry.Protocol.Device; + +namespace Sentry.Maui.Internal; + +internal static class MauiDeviceData +{ + public static void ApplyMauiDeviceData(this Device device) + { + // TODO: Add more device data where indicated + + // https://docs.microsoft.com/dotnet/maui/platform-integration/device/information + var deviceInfo = DeviceInfo.Current; + device.Name ??= deviceInfo.Name; + device.Manufacturer ??= deviceInfo.Manufacturer; + device.Model ??= deviceInfo.Model; + device.DeviceType ??= deviceInfo.Idiom.ToString(); + device.Simulator ??= deviceInfo.DeviceType switch + { + DeviceType.Virtual => true, + DeviceType.Physical => false, + _ => null + }; + // device.Brand ??= ? + // device.Family ??= ? + // device.ModelId ??= ? + // device.Architecture ??= ? + // ? = deviceInfo.Platform; + // ? = deviceInfo.VersionString; + + // https://docs.microsoft.com/dotnet/maui/platform-integration/device/battery + var battery = Battery.Default; + device.BatteryLevel ??= battery.ChargeLevel < 0 ? null : (short)battery.ChargeLevel; + device.BatteryStatus ??= battery.State.ToString(); + device.IsCharging ??= battery.State switch + { + BatteryState.Unknown => null, + BatteryState.Charging => true, + _ => false + }; + + // https://docs.microsoft.com/dotnet/maui/platform-integration/communication/networking#using-connectivity + var connectivity = Connectivity.Current; + device.IsOnline ??= connectivity.NetworkAccess == NetworkAccess.Internet; + + // https://docs.microsoft.com/dotnet/maui/platform-integration/device/display + var display = DeviceDisplay.Current.MainDisplayInfo; + device.ScreenResolution ??= $"{(int)display.Width}x{(int)display.Height}"; + device.ScreenDensity ??= (float)display.Density; + device.Orientation ??= display.Orientation switch + { + DisplayOrientation.Portrait => DeviceOrientation.Portrait, + DisplayOrientation.Landscape => DeviceOrientation.Landscape, + _ => null + }; + // device.ScreenDpi ??= ? + // ? = display.RefreshRate; + // ? = display.Rotation; + + // https://docs.microsoft.com/dotnet/maui/platform-integration/device/vibrate + device.SupportsVibration ??= Vibration.Default.IsSupported; + + // https://docs.microsoft.com/dotnet/maui/platform-integration/device/sensors + device.SupportsAccelerometer ??= Accelerometer.IsSupported; + device.SupportsGyroscope ??= Gyroscope.IsSupported; + + // https://docs.microsoft.com/dotnet/maui/platform-integration/device/geolocation + // TODO: How to get without actually trying to make a location request? + // device.SupportsLocationService ??= Geolocation.Default.??? + + // device.SupportsAudio ??= ? + + // device.MemorySize ??= + // device.FreeMemory ??= + // device.UsableMemory ??= + // device.LowMemory ??= + + // device.StorageSize ??= + // device.FreeStorage ??= + // device.ExternalStorageSize ??= + // device.ExternalFreeStorage ??= + + // device.BootTime ??= + // device.DeviceUniqueIdentifier ??= + + //device.CpuDescription ??= ? + //device.ProcessorCount ??= ? + //device.ProcessorFrequency ??= ? + } +} diff --git a/src/Sentry.Maui/Internal/SentryMauiEventProcessor.cs b/src/Sentry.Maui/Internal/SentryMauiEventProcessor.cs index 973ddde7ac..147b6d4b42 100644 --- a/src/Sentry.Maui/Internal/SentryMauiEventProcessor.cs +++ b/src/Sentry.Maui/Internal/SentryMauiEventProcessor.cs @@ -6,9 +6,9 @@ internal class SentryMauiEventProcessor : ISentryEventProcessor { public SentryEvent Process(SentryEvent @event) { - // Set SDK name and version for MAUI @event.Sdk.Name = Constants.SdkName; @event.Sdk.Version = Constants.SdkVersion; + @event.Contexts.Device.ApplyMauiDeviceData(); return @event; } From 04d25bbee230795524e582c22c5e4be8f18d324d Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Tue, 14 Jun 2022 14:13:52 -0700 Subject: [PATCH 5/9] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e21476506e..a52995b6a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ - Continue with adding MAUI support ([#1670](https://github.com/getsentry/sentry-dotnet/pull/1670)) - MAUI events become extra context in Sentry events ([#1706](https://github.com/getsentry/sentry-dotnet/pull/1706)) - Add options for PII breadcrumbs from MAUI events ([#1709](https://github.com/getsentry/sentry-dotnet/pull/1709)) + - Add device information to the event context ([#1713](https://github.com/getsentry/sentry-dotnet/pull/1713)) - Added a new `net6.0-android` target for the `Sentry` core library, which bundles the [Sentry Android SDK](https://docs.sentry.io/platforms/android/): - Initial .NET 6 Android support ([#1288](https://github.com/getsentry/sentry-dotnet/pull/1288)) - Update Android Support ([#1669](https://github.com/getsentry/sentry-dotnet/pull/1669)) From 96ab3d54b244d827e8d32c5f2ff0bd62093177ad Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Tue, 14 Jun 2022 14:55:45 -0700 Subject: [PATCH 6/9] Fix OOM crash --- samples/Sentry.Samples.Maui/MauiProgram.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/Sentry.Samples.Maui/MauiProgram.cs b/samples/Sentry.Samples.Maui/MauiProgram.cs index a885ec832c..7d1d764c17 100644 --- a/samples/Sentry.Samples.Maui/MauiProgram.cs +++ b/samples/Sentry.Samples.Maui/MauiProgram.cs @@ -9,7 +9,7 @@ public static MauiApp CreateMauiApp() => { options.Dsn = "https://eb18e953812b41c3aeb042e666fd3b5c@o447951.ingest.sentry.io/5428537"; options.Debug = true; - options.MaxBreadcrumbs = int.MaxValue; // TODO: reduce breadcrumbs, remove this + options.MaxBreadcrumbs = 1000; // TODO: reduce breadcrumbs, remove this }) .ConfigureFonts(fonts => { From 2019d9b6ca4e041ef60309ae5f4a5c55d1f1cff7 Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Tue, 14 Jun 2022 15:21:21 -0700 Subject: [PATCH 7/9] Fix unit test --- src/Sentry.Maui/Internal/MauiDeviceData.cs | 151 ++++++++++-------- .../Internal/SentryMauiEventProcessor.cs | 9 +- .../Internal/SentryMauiOptionsSetup.cs | 2 +- 3 files changed, 92 insertions(+), 70 deletions(-) diff --git a/src/Sentry.Maui/Internal/MauiDeviceData.cs b/src/Sentry.Maui/Internal/MauiDeviceData.cs index 87dc6e6cc1..7a25f6a8ed 100644 --- a/src/Sentry.Maui/Internal/MauiDeviceData.cs +++ b/src/Sentry.Maui/Internal/MauiDeviceData.cs @@ -1,3 +1,4 @@ +using Sentry.Extensibility; using Sentry.Protocol; using Device = Sentry.Protocol.Device; @@ -5,86 +6,100 @@ namespace Sentry.Maui.Internal; internal static class MauiDeviceData { - public static void ApplyMauiDeviceData(this Device device) + public static void ApplyMauiDeviceData(this Device device, IDiagnosticLogger? logger) { - // TODO: Add more device data where indicated - - // https://docs.microsoft.com/dotnet/maui/platform-integration/device/information - var deviceInfo = DeviceInfo.Current; - device.Name ??= deviceInfo.Name; - device.Manufacturer ??= deviceInfo.Manufacturer; - device.Model ??= deviceInfo.Model; - device.DeviceType ??= deviceInfo.Idiom.ToString(); - device.Simulator ??= deviceInfo.DeviceType switch + try { - DeviceType.Virtual => true, - DeviceType.Physical => false, - _ => null - }; - // device.Brand ??= ? - // device.Family ??= ? - // device.ModelId ??= ? - // device.Architecture ??= ? - // ? = deviceInfo.Platform; - // ? = deviceInfo.VersionString; + // TODO: Add more device data where indicated - // https://docs.microsoft.com/dotnet/maui/platform-integration/device/battery - var battery = Battery.Default; - device.BatteryLevel ??= battery.ChargeLevel < 0 ? null : (short)battery.ChargeLevel; - device.BatteryStatus ??= battery.State.ToString(); - device.IsCharging ??= battery.State switch - { - BatteryState.Unknown => null, - BatteryState.Charging => true, - _ => false - }; + // https://docs.microsoft.com/dotnet/maui/platform-integration/device/information + var deviceInfo = DeviceInfo.Current; + if (deviceInfo.Platform == DevicePlatform.Unknown) + { + // return early so we don't get NotImplementedExceptions (i.e., in unit tests, etc.) + return; + } + device.Name ??= deviceInfo.Name; + device.Manufacturer ??= deviceInfo.Manufacturer; + device.Model ??= deviceInfo.Model; + device.DeviceType ??= deviceInfo.Idiom.ToString(); + device.Simulator ??= deviceInfo.DeviceType switch + { + DeviceType.Virtual => true, + DeviceType.Physical => false, + _ => null + }; + // device.Brand ??= ? + // device.Family ??= ? + // device.ModelId ??= ? + // device.Architecture ??= ? + // ? = deviceInfo.Platform; + // ? = deviceInfo.VersionString; - // https://docs.microsoft.com/dotnet/maui/platform-integration/communication/networking#using-connectivity - var connectivity = Connectivity.Current; - device.IsOnline ??= connectivity.NetworkAccess == NetworkAccess.Internet; + // https://docs.microsoft.com/dotnet/maui/platform-integration/device/battery + var battery = Battery.Default; + device.BatteryLevel ??= battery.ChargeLevel < 0 ? null : (short)battery.ChargeLevel; + device.BatteryStatus ??= battery.State.ToString(); + device.IsCharging ??= battery.State switch + { + BatteryState.Unknown => null, + BatteryState.Charging => true, + _ => false + }; - // https://docs.microsoft.com/dotnet/maui/platform-integration/device/display - var display = DeviceDisplay.Current.MainDisplayInfo; - device.ScreenResolution ??= $"{(int)display.Width}x{(int)display.Height}"; - device.ScreenDensity ??= (float)display.Density; - device.Orientation ??= display.Orientation switch - { - DisplayOrientation.Portrait => DeviceOrientation.Portrait, - DisplayOrientation.Landscape => DeviceOrientation.Landscape, - _ => null - }; - // device.ScreenDpi ??= ? - // ? = display.RefreshRate; - // ? = display.Rotation; + // https://docs.microsoft.com/dotnet/maui/platform-integration/communication/networking#using-connectivity + var connectivity = Connectivity.Current; + device.IsOnline ??= connectivity.NetworkAccess == NetworkAccess.Internet; - // https://docs.microsoft.com/dotnet/maui/platform-integration/device/vibrate - device.SupportsVibration ??= Vibration.Default.IsSupported; + // https://docs.microsoft.com/dotnet/maui/platform-integration/device/display + var display = DeviceDisplay.Current.MainDisplayInfo; + device.ScreenResolution ??= $"{(int)display.Width}x{(int)display.Height}"; + device.ScreenDensity ??= (float)display.Density; + device.Orientation ??= display.Orientation switch + { + DisplayOrientation.Portrait => DeviceOrientation.Portrait, + DisplayOrientation.Landscape => DeviceOrientation.Landscape, + _ => null + }; + // device.ScreenDpi ??= ? + // ? = display.RefreshRate; + // ? = display.Rotation; - // https://docs.microsoft.com/dotnet/maui/platform-integration/device/sensors - device.SupportsAccelerometer ??= Accelerometer.IsSupported; - device.SupportsGyroscope ??= Gyroscope.IsSupported; + // https://docs.microsoft.com/dotnet/maui/platform-integration/device/vibrate + device.SupportsVibration ??= Vibration.Default.IsSupported; - // https://docs.microsoft.com/dotnet/maui/platform-integration/device/geolocation - // TODO: How to get without actually trying to make a location request? - // device.SupportsLocationService ??= Geolocation.Default.??? + // https://docs.microsoft.com/dotnet/maui/platform-integration/device/sensors + device.SupportsAccelerometer ??= Accelerometer.IsSupported; + device.SupportsGyroscope ??= Gyroscope.IsSupported; - // device.SupportsAudio ??= ? + // https://docs.microsoft.com/dotnet/maui/platform-integration/device/geolocation + // TODO: How to get without actually trying to make a location request? + // device.SupportsLocationService ??= Geolocation.Default.??? - // device.MemorySize ??= - // device.FreeMemory ??= - // device.UsableMemory ??= - // device.LowMemory ??= + // device.SupportsAudio ??= ? - // device.StorageSize ??= - // device.FreeStorage ??= - // device.ExternalStorageSize ??= - // device.ExternalFreeStorage ??= + // device.MemorySize ??= + // device.FreeMemory ??= + // device.UsableMemory ??= + // device.LowMemory ??= - // device.BootTime ??= - // device.DeviceUniqueIdentifier ??= + // device.StorageSize ??= + // device.FreeStorage ??= + // device.ExternalStorageSize ??= + // device.ExternalFreeStorage ??= - //device.CpuDescription ??= ? - //device.ProcessorCount ??= ? - //device.ProcessorFrequency ??= ? + // device.BootTime ??= + // device.DeviceUniqueIdentifier ??= + + //device.CpuDescription ??= ? + //device.ProcessorCount ??= ? + //device.ProcessorFrequency ??= ? + + } + catch (Exception ex) + { + // Log, but swallow the exception so we can continue sending events + logger?.LogError("Error getting MAUI device information.", ex); + } } } diff --git a/src/Sentry.Maui/Internal/SentryMauiEventProcessor.cs b/src/Sentry.Maui/Internal/SentryMauiEventProcessor.cs index 147b6d4b42..909d90772f 100644 --- a/src/Sentry.Maui/Internal/SentryMauiEventProcessor.cs +++ b/src/Sentry.Maui/Internal/SentryMauiEventProcessor.cs @@ -4,11 +4,18 @@ namespace Sentry.Maui.Internal; internal class SentryMauiEventProcessor : ISentryEventProcessor { + private readonly SentryMauiOptions _options; + + public SentryMauiEventProcessor(SentryMauiOptions options) + { + _options = options; + } + public SentryEvent Process(SentryEvent @event) { @event.Sdk.Name = Constants.SdkName; @event.Sdk.Version = Constants.SdkVersion; - @event.Contexts.Device.ApplyMauiDeviceData(); + @event.Contexts.Device.ApplyMauiDeviceData(_options.DiagnosticLogger); return @event; } diff --git a/src/Sentry.Maui/Internal/SentryMauiOptionsSetup.cs b/src/Sentry.Maui/Internal/SentryMauiOptionsSetup.cs index da424b19f2..4bec89bb30 100644 --- a/src/Sentry.Maui/Internal/SentryMauiOptionsSetup.cs +++ b/src/Sentry.Maui/Internal/SentryMauiOptionsSetup.cs @@ -20,6 +20,6 @@ public override void Configure(SentryMauiOptions options) options.IsGlobalModeEnabled = true; // We'll use an event processor to set things like SDK name - options.AddEventProcessor(new SentryMauiEventProcessor()); + options.AddEventProcessor(new SentryMauiEventProcessor(options)); } } From ac9100d92189131f5ecd02170e2abbd292131098 Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Tue, 14 Jun 2022 15:41:39 -0700 Subject: [PATCH 8/9] Get some information directly --- src/Sentry/Android/AndroidEventProcessor.cs | 30 +++++++++++++------ .../Android/Extensions/DeviceExtensions.cs | 30 +++++++++++++++---- 2 files changed, 46 insertions(+), 14 deletions(-) diff --git a/src/Sentry/Android/AndroidEventProcessor.cs b/src/Sentry/Android/AndroidEventProcessor.cs index 5358b3a056..29cb72f4f3 100644 --- a/src/Sentry/Android/AndroidEventProcessor.cs +++ b/src/Sentry/Android/AndroidEventProcessor.cs @@ -6,7 +6,7 @@ namespace Sentry.Android; internal class AndroidEventProcessor : ISentryEventProcessor, IDisposable { - private readonly Java.IEventProcessor _androidProcessor; + private readonly Java.IEventProcessor? _androidProcessor; private readonly Java.Hint _hint = new(); public AndroidEventProcessor(SentryAndroidOptions androidOptions) @@ -14,25 +14,37 @@ public AndroidEventProcessor(SentryAndroidOptions androidOptions) _androidProcessor = androidOptions.EventProcessors.OfType() .Where(x => x.Class.Name == "io.sentry.android.core.DefaultAndroidEventProcessor") .Cast() - .First(); + .FirstOrDefault(); } public SentryEvent Process(SentryEvent @event) { - // Run a fake event through the Android processor, so we can get context info from the Android SDK. - // We'll want to do this every time, so that all information is current. (ex: device orientation) - using var e = new Java.SentryEvent(); - _androidProcessor.Process(e, _hint); + // Get what information we can ourselves first + @event.Contexts.Device.ApplyFromAndroidRuntime(); - // Copy what we need to the managed event - e.Contexts.Device?.ApplyTo(@event.Contexts.Device); + // Copy more information from the Android SDK + if (_androidProcessor is { } androidProcessor) + { + // TODO: Can we gather more device data directly and remove this? + + // Run a fake event through the Android processor, so we can get context info from the Android SDK. + // We'll want to do this every time, so that all information is current. (ex: device orientation) + using var e = new Java.SentryEvent(); + androidProcessor.Process(e, _hint); + + // Copy what we need to the managed event + if (e.Contexts.Device is { } device) + { + @event.Contexts.Device.ApplyFromSentryAndroidSdk(device); + } + } return @event; } public void Dispose() { - _androidProcessor.Dispose(); + _androidProcessor?.Dispose(); _hint.Dispose(); } } diff --git a/src/Sentry/Android/Extensions/DeviceExtensions.cs b/src/Sentry/Android/Extensions/DeviceExtensions.cs index ab5f2b3034..48a231c07e 100644 --- a/src/Sentry/Android/Extensions/DeviceExtensions.cs +++ b/src/Sentry/Android/Extensions/DeviceExtensions.cs @@ -4,15 +4,35 @@ namespace Sentry.Android.Extensions; internal static class DeviceExtensions { - public static void ApplyTo(this Java.Protocol.Device d, Device device) + public static void ApplyFromAndroidRuntime(this Device device) { + device.Manufacturer ??= AndroidBuild.Manufacturer; + device.Brand ??= AndroidBuild.Brand; + device.Model ??= AndroidBuild.Model; + + if (AndroidBuild.SupportedAbis is { } abis) + { + device.Architecture ??= abis[0]; + } + else + { +#pragma warning disable CS0618 // Type or member is obsolete + device.Architecture ??= AndroidBuild.CpuAbi; +#pragma warning restore CS0618 // Type or member is obsolete + } + } + + public static void ApplyFromSentryAndroidSdk(this Device device, Java.Protocol.Device d) + { + // We already have these above + // device.Manufacturer ??= d.Manufacturer; + // device.Brand ??= d.Brand; + // device.Model ??= d.Model; + // device.Architecture ??= d.GetArchs()?.FirstOrDefault(); + device.Name ??= d.Name; - device.Manufacturer ??= d.Manufacturer; - device.Brand ??= d.Brand; device.Family ??= d.Family; - device.Model ??= d.Model; device.ModelId ??= d.ModelId; - device.Architecture ??= d.GetArchs()?.FirstOrDefault(); device.BatteryLevel ??= d.BatteryLevel?.ShortValue(); device.IsCharging ??= d.IsCharging()?.BooleanValue(); device.IsOnline ??= d.IsOnline()?.BooleanValue(); From 9344db2facf2e900fc23321da01925d7024698c9 Mon Sep 17 00:00:00 2001 From: Matt Johnson-Pint Date: Tue, 14 Jun 2022 17:00:11 -0700 Subject: [PATCH 9/9] Fix potential proguard issue --- src/Sentry/Android/AndroidEventProcessor.cs | 10 ++++++++-- src/Sentry/Android/Transforms/Metadata.xml | 3 +++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/Sentry/Android/AndroidEventProcessor.cs b/src/Sentry/Android/AndroidEventProcessor.cs index 29cb72f4f3..c5c17b9deb 100644 --- a/src/Sentry/Android/AndroidEventProcessor.cs +++ b/src/Sentry/Android/AndroidEventProcessor.cs @@ -1,6 +1,5 @@ using Sentry.Android.Extensions; using Sentry.Extensibility; -using Sentry.Protocol; namespace Sentry.Android; @@ -11,10 +10,17 @@ internal class AndroidEventProcessor : ISentryEventProcessor, IDisposable public AndroidEventProcessor(SentryAndroidOptions androidOptions) { + // Locate the Android SDK's default event processor by its class + // NOTE: This approach avoids hardcoding the class name (which could be obfuscated by proguard) _androidProcessor = androidOptions.EventProcessors.OfType() - .Where(x => x.Class.Name == "io.sentry.android.core.DefaultAndroidEventProcessor") + .Where(o => o.Class == JavaClass.FromType(typeof(DefaultAndroidEventProcessor))) .Cast() .FirstOrDefault(); + + // TODO: This would be cleaner, but doesn't compile. Figure out why. + // _androidProcessor = androidOptions.EventProcessors + // .OfType() + // .FirstOrDefault(); } public SentryEvent Process(SentryEvent @event) diff --git a/src/Sentry/Android/Transforms/Metadata.xml b/src/Sentry/Android/Transforms/Metadata.xml index 9c6bc0662a..37056dec78 100644 --- a/src/Sentry/Android/Transforms/Metadata.xml +++ b/src/Sentry/Android/Transforms/Metadata.xml @@ -46,6 +46,9 @@ SentryTraceHeaderName TraceStateHeaderName + + internal +