diff --git a/CHANGELOG.md b/CHANGELOG.md index 9dc239db9..5540cb751 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - Added a fallback for user.id on Android and iOS in case none could be extracted from the native layer ([#1710](https://github.com/getsentry/sentry-unity/pull/1710)) - The IL2CPP exception processor no longer fails when the native support has been disabled ([#1708](https://github.com/getsentry/sentry-unity/pull/1708)) +- The SDK now checks whether the Android SDK is available before attempting to initialize it. This prevents `AndroidJavaException: java.lang.ClassNotFoundException: io.sentry.Sentry` from being thrown ([#1714](https://github.com/getsentry/sentry-unity/pull/1714)) ### Dependencies diff --git a/src/Sentry.Unity.Android/SentryJava.cs b/src/Sentry.Unity.Android/SentryJava.cs index 5ac0b9d7a..2e362e7e8 100644 --- a/src/Sentry.Unity.Android/SentryJava.cs +++ b/src/Sentry.Unity.Android/SentryJava.cs @@ -25,6 +25,7 @@ public void WriteScope( string? GpuVendorId, bool? GpuMultiThreadedRendering, string? GpuGraphicsShaderLevel); + public bool IsSentryJavaPresent(); } /// @@ -37,6 +38,8 @@ public void WriteScope( /// internal class SentryJava : ISentryJava { + private static AndroidJavaObject GetSentryJava() => new AndroidJavaClass("io.sentry.Sentry"); + public string? GetInstallationId(IJniExecutor jniExecutor) { return jniExecutor.Run(() => @@ -77,8 +80,6 @@ public void Close(IJniExecutor jniExecutor) }); } - private static AndroidJavaObject GetSentryJava() => new AndroidJavaClass("io.sentry.Sentry"); - public void WriteScope( IJniExecutor jniExecutor, int? GpuId, @@ -107,14 +108,12 @@ public void WriteScope( using var integer = new AndroidJavaObject("java.lang.Integer", intVendorId); gpu.Set("vendorId", integer); } - gpu.SetIfNotNull("vendorName", GpuVendorName); gpu.SetIfNotNull("memorySize", GpuMemorySize); gpu.SetIfNotNull("apiType", GpuApiType); gpu.SetIfNotNull("multiThreadedRendering", GpuMultiThreadedRendering); gpu.SetIfNotNull("version", GpuVersion); gpu.SetIfNotNull("npotSupport", GpuNpotSupport); - using var sentry = GetSentryJava(); sentry.CallStatic("configureScope", new ScopeCallback(scope => { @@ -124,6 +123,20 @@ public void WriteScope( }); } + public bool IsSentryJavaPresent() + { + try + { + _ = GetSentryJava(); + } + catch (AndroidJavaException) + { + return false; + } + + return true; + } + // Implements the io.sentry.ScopeCallback interface. internal class ScopeCallback : AndroidJavaProxy { @@ -175,10 +188,8 @@ public static void SetIfNotNull(this AndroidJavaObject javaObject, string pro } } } - public static void SetIfNotNull(this AndroidJavaObject javaObject, string property, int? value) => SetIfNotNull(javaObject, property, value, "java.lang.Integer"); - public static void SetIfNotNull(this AndroidJavaObject javaObject, string property, bool? value) => SetIfNotNull(javaObject, property, value, "java.lang.Boolean"); } diff --git a/src/Sentry.Unity.Android/SentryNativeAndroid.cs b/src/Sentry.Unity.Android/SentryNativeAndroid.cs index 7fe3c6d1a..2a4ba3086 100644 --- a/src/Sentry.Unity.Android/SentryNativeAndroid.cs +++ b/src/Sentry.Unity.Android/SentryNativeAndroid.cs @@ -10,7 +10,7 @@ namespace Sentry.Unity.Android; public static class SentryNativeAndroid { internal static IJniExecutor? JniExecutor; - internal static ISentryJava? SentryJava; + internal static ISentryJava SentryJava = new SentryJava(); /// /// Configures the native Android support. @@ -26,8 +26,16 @@ public static void Configure(SentryUnityOptions options, ISentryUnityInfo sentry return; } + if (!SentryJava.IsSentryJavaPresent()) + { + options.DiagnosticLogger?.LogError("Android Native Support has been enabled but the " + + "Sentry Java SDK is missing. This could have been caused by a mismatching" + + "build time / runtime configuration. Please make sure you have " + + "Android Native Support enabled during build time."); + return; + } + JniExecutor ??= new JniExecutor(); - SentryJava ??= new SentryJava(); options.NativeContextWriter = new NativeContextWriter(JniExecutor, SentryJava); options.ScopeObserver = new AndroidJavaScopeObserver(options, JniExecutor); @@ -39,7 +47,8 @@ public static void Configure(SentryUnityOptions options, ISentryUnityInfo sentry { // Could happen if the Android SDK wasn't initialized before the .NET layer. options.DiagnosticLogger? - .LogWarning("Unclear from the native SDK if the previous run was a crash. Assuming it was not."); + .LogWarning( + "Unclear from the native SDK if the previous run was a crash. Assuming it was not."); crashedLastRun = false; } else @@ -71,7 +80,8 @@ public static void Configure(SentryUnityOptions options, ISentryUnityInfo sentry if (string.IsNullOrEmpty(options.DefaultUserId)) { // In case we can't get an installation ID we create one and sync that down to the native layer - options.DiagnosticLogger?.LogDebug("Failed to fetch 'Installation ID' from the native SDK. Creating new 'Default User ID'."); + options.DiagnosticLogger?.LogDebug( + "Failed to fetch 'Installation ID' from the native SDK. Creating new 'Default User ID'."); // We fall back to Unity's Analytics Session Info: https://docs.unity3d.com/ScriptReference/Analytics.AnalyticsSessionInfo-userId.html // It's a randomly generated GUID that gets created immediately after installation helping @@ -93,6 +103,11 @@ public static void Configure(SentryUnityOptions options, ISentryUnityInfo sentry /// public static void Close(IDiagnosticLogger? logger = null) { + if (!SentryJava.IsSentryJavaPresent()) + { + return; + } + // Sentry Native is initialized and closed by the Java SDK, no need to call into it directly logger?.LogDebug("Closing the sentry-java SDK"); diff --git a/test/Sentry.Unity.Android.Tests/TestSentryJava.cs b/test/Sentry.Unity.Android.Tests/TestSentryJava.cs index 9a03e44dd..26d1d8d0a 100644 --- a/test/Sentry.Unity.Android.Tests/TestSentryJava.cs +++ b/test/Sentry.Unity.Android.Tests/TestSentryJava.cs @@ -5,15 +5,9 @@ internal class TestSentryJava : ISentryJava public string? InstallationId { get; set; } public bool? IsCrashedLastRun { get; set; } - public string? GetInstallationId(IJniExecutor jniExecutor) - { - return InstallationId; - } + public string? GetInstallationId(IJniExecutor jniExecutor) => InstallationId; - public bool? CrashedLastRun(IJniExecutor jniExecutor) - { - return IsCrashedLastRun; - } + public bool? CrashedLastRun(IJniExecutor jniExecutor) => IsCrashedLastRun; public void Close(IJniExecutor jniExecutor) { } @@ -35,4 +29,6 @@ public void WriteScope( bool? GpuMultiThreadedRendering, string? GpuGraphicsShaderLevel) { } + + public bool IsSentryJavaPresent() => true; }