diff --git a/CHANGELOG.md b/CHANGELOG.md
index 319349246..742bedb03 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,7 @@
### Fixes
- Fixed a `NoSuchFieldError` during initialization on Android when setting the `sample rate`. ([#2838](https://github.com/getsentry/sentry-unity/issues/2838))
+- Fixed a potential startup crash when targeting Android. The SDK would still set up native support even without a DSN provided. This would cause `sentry-java` to auto-initialize during startup via the `SentryInitProvider`. The SDK now verifies the presence of a DSN at build time, skipping native support when it's missing and logs accordingly. ([#2846](https://github.com/getsentry/sentry-unity/issues/2846))
## 4.10.0
diff --git a/src/Sentry.Unity.Editor/Android/AndroidManifestConfiguration.cs b/src/Sentry.Unity.Editor/Android/AndroidManifestConfiguration.cs
index 85c5052d5..b4fdf20f6 100644
--- a/src/Sentry.Unity.Editor/Android/AndroidManifestConfiguration.cs
+++ b/src/Sentry.Unity.Editor/Android/AndroidManifestConfiguration.cs
@@ -48,6 +48,8 @@ public class AndroidManifestConfiguration
private readonly bool _isDevelopmentBuild;
private readonly ScriptingImplementation _scriptingImplementation;
+ private bool? _androidSdkEnabled;
+
public AndroidManifestConfiguration()
: this(
SentryScriptableObject.LoadOptions,
@@ -111,43 +113,60 @@ public void OnPostGenerateGradleAndroidProject(string basePath)
SetupProguard(gradleProjectPath);
}
- internal void ModifyManifest(string basePath)
- {
- var manifestPath = GetManifestPath(basePath);
- if (!File.Exists(manifestPath))
- {
- throw new FileNotFoundException("Can't configure native Android SDK nor set auto-init:false.",
- manifestPath);
- }
+ ///
+ /// Whether this build ships the Android SDK. Shared with because an app that
+ /// carries `sentry-android-core` without the matching manifest entries auto-initializes without a DSN,
+ /// which crashes it on startup.
+ ///
+ private bool AndroidSdkEnabled => _androidSdkEnabled ??= EvaluateAndroidSdkEnabled();
- var enableNativeSupport = true;
+ private bool EvaluateAndroidSdkEnabled()
+ {
if (_options is null)
{
_logger.LogWarning("Android native support disabled because Sentry has not been configured. " +
"You can do that through the editor: {0}", SentryWindow.EditorMenuPath);
- enableNativeSupport = false;
+ return false;
}
- else if (!_options.IsValid())
+
+ if (!_options.IsValid())
{
_logger.LogDebug("Android native support disabled.");
- enableNativeSupport = false;
+ return false;
}
- else if (!_options.AndroidNativeSupportEnabled)
+
+ if (!_options.AndroidNativeSupportEnabled)
{
_logger.LogDebug("Android native support disabled through the options.");
- enableNativeSupport = false;
+ return false;
+ }
+
+ return true;
+ }
+
+ internal void ModifyManifest(string basePath)
+ {
+ var manifestPath = GetManifestPath(basePath);
+ if (!File.Exists(manifestPath))
+ {
+ throw new FileNotFoundException("Can't configure native Android SDK nor set auto-init:false.",
+ manifestPath);
}
var androidManifest = new AndroidManifest(manifestPath, _logger);
androidManifest.RemovePreviousConfigurations();
+ androidManifest.AddDisclaimerComment();
- if (!enableNativeSupport)
+ if (!AndroidSdkEnabled)
{
+ // Should the SDK end up in the app regardless, this keeps it from crashing on startup.
+ _logger.LogDebug("Setting 'auto-init' to 'false'. The Android SDK is not part of this build.");
+ androidManifest.SetAutoInit(false);
+ _ = androidManifest.Save();
+
return;
}
- androidManifest.AddDisclaimerComment();
-
if (_options?.AndroidNativeInitializationType is NativeInitializationType.Runtime)
{
_logger.LogDebug("Setting 'auto-init' to 'false'. The Android SDK will be initialized at runtime.");
@@ -239,7 +258,7 @@ internal void CopyAndroidSdkToGradleProject(string unityProjectPath, string grad
var androidSdkPath = Path.Combine(unityProjectPath, "Packages", SentryPackageInfo.GetName(), "Plugins", "Android", "Sentry~");
var targetPath = Path.Combine(gradlePath, "unityLibrary", "libs");
- if (_options is { Enabled: true, AndroidNativeSupportEnabled: true })
+ if (AndroidSdkEnabled)
{
if (!Directory.Exists(androidSdkPath))
{
@@ -260,6 +279,13 @@ internal void CopyAndroidSdkToGradleProject(string unityProjectPath, string grad
}
else
{
+ if (!Directory.Exists(androidSdkPath))
+ {
+ // A build that does not ship the SDK has no reason to fail over a missing SDK.
+ _logger.LogDebug("Failed to find the Android SDK at '{0}'. Nothing to remove.", androidSdkPath);
+ return;
+ }
+
_logger.LogInfo("Removing the Android SDK from the output project.");
foreach (var file in Directory.GetFiles(androidSdkPath))
{
@@ -275,7 +301,7 @@ internal void CopyAndroidSdkToGradleProject(string unityProjectPath, string grad
internal void AddAndroidSdkDependencies(string gradleProjectPath)
{
var tool = new GradleSetup(_logger, gradleProjectPath);
- var nativeSupportEnabled = _options is { Enabled: true, AndroidNativeSupportEnabled: true };
+ var nativeSupportEnabled = AndroidSdkEnabled;
try
{
@@ -358,7 +384,7 @@ internal void SetupSymbolsUpload(string unityProjectPath, string gradleProjectPa
private void SetupProguard(string gradleProjectPath)
{
var tool = new ProguardSetup(_logger, gradleProjectPath);
- var nativeSupportEnabled = _options is { Enabled: true, AndroidNativeSupportEnabled: true };
+ var nativeSupportEnabled = AndroidSdkEnabled;
try
{
diff --git a/test/Sentry.Unity.Editor.Tests/Android/AndroidManifestConfigurationTests.cs b/test/Sentry.Unity.Editor.Tests/Android/AndroidManifestConfigurationTests.cs
index 0be4840a1..fe75ebe6e 100644
--- a/test/Sentry.Unity.Editor.Tests/Android/AndroidManifestConfigurationTests.cs
+++ b/test/Sentry.Unity.Editor.Tests/Android/AndroidManifestConfigurationTests.cs
@@ -113,6 +113,38 @@ public void ModifyManifest_UnityOptions_EnabledWithoutDsn_LogWarningAndDoesNotAd
Assert.False(manifest.Contains("io.sentry.dsn"));
}
+ [Test]
+ [TestCase(null)]
+ [TestCase("")]
+ public void ModifyManifest_AndroidSdkDisabled_SetsAutoInitToFalse(string? dsn)
+ {
+ _fixture.SentryUnityOptions!.Dsn = dsn;
+ var sut = _fixture.GetSut();
+ var manifest = WithAndroidManifest(basePath => sut.ModifyManifest(basePath));
+
+ StringAssert.Contains("", manifest);
+ }
+
+ [Test]
+ public void CopyAndroidSdkToGradleProject_EnabledWithoutDsn_RemovesAndroidSdkFromGradleProject()
+ {
+ var fakeProjectPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
+ var unityProjectPath = Path.Combine(fakeProjectPath, "UnityProject");
+ var gradleProjectPath = Path.Combine(fakeProjectPath, "GradleProject");
+ DebugSymbolUploadTests.SetupFakeProject(fakeProjectPath);
+ var androidSdk = Path.Combine(gradleProjectPath, "unityLibrary", "libs", "androidSdk.jar");
+ File.Create(androidSdk).Close();
+
+ _fixture.SentryUnityOptions!.Dsn = string.Empty;
+ var sut = _fixture.GetSut();
+
+ sut.CopyAndroidSdkToGradleProject(unityProjectPath, gradleProjectPath);
+
+ Assert.IsFalse(File.Exists(androidSdk));
+
+ Directory.Delete(fakeProjectPath, true);
+ }
+
[Test]
public void ModifyManifest_UnityOptions_AndroidNativeSupportEnabledFalse_LogDebugAndDoesNotAddSentry()
{
@@ -514,6 +546,25 @@ public void CopyAndroidSdkToGradleProject_AndroidNativeSupportDisabledButSdkAlre
Directory.Delete(fakeProjectPath, true);
}
+ [Test]
+ public void CopyAndroidSdkToGradleProject_AndroidSdkDisabledAndSourceMissing_DoesNotThrow()
+ {
+ var fakeProjectPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
+ var unityProjectPath = Path.Combine(fakeProjectPath, "UnityProject");
+ var gradleProjectPath = Path.Combine(fakeProjectPath, "GradleProject");
+ DebugSymbolUploadTests.SetupFakeProject(fakeProjectPath);
+ Directory.Delete(
+ Path.Combine(unityProjectPath, "Packages", SentryPackageInfo.GetName(), "Plugins", "Android", "Sentry~"),
+ true);
+
+ _fixture.SentryUnityOptions!.AndroidNativeSupportEnabled = false;
+ var sut = _fixture.GetSut();
+
+ Assert.DoesNotThrow(() => sut.CopyAndroidSdkToGradleProject(unityProjectPath, gradleProjectPath));
+
+ Directory.Delete(fakeProjectPath, true);
+ }
+
[Test]
public void CopyAndroidSdkToGradleProject_SdkAlreadyExists_OverwritesExistingSdk()
{