diff --git a/src/Mono.Android.Runtime/Android.Runtime/AndroidEnvironmentInternal.cs b/src/Mono.Android.Runtime/Android.Runtime/AndroidEnvironmentInternal.cs index 198e6c92423..682959e90e5 100644 --- a/src/Mono.Android.Runtime/Android.Runtime/AndroidEnvironmentInternal.cs +++ b/src/Mono.Android.Runtime/Android.Runtime/AndroidEnvironmentInternal.cs @@ -1,12 +1,15 @@ using System; +using System.ComponentModel; namespace Android.Runtime { - internal static class AndroidEnvironmentInternal + [EditorBrowsable (EditorBrowsableState.Never)] + public static class AndroidEnvironmentInternal { internal static Action? UnhandledExceptionHandler; - internal static void UnhandledException (Exception e) + [EditorBrowsable (EditorBrowsableState.Never)] + public static void UnhandledException (Exception e) { if (UnhandledExceptionHandler == null) { return; diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest2.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest2.cs index e26bce1dc50..da233a70a47 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest2.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest2.cs @@ -2153,5 +2153,71 @@ public void Plugin_Maui_Audio ([Values] AndroidRuntime runtime) const string className = "Lcrc64467b05f37239e7a6/StreamMediaDataSource;"; Assert.IsTrue (DexUtils.ContainsClass (className, dexFile, AndroidSdkPath), $"`{dexFile}` should include `{className}`!"); } + + [Test] + public void MarshalMethodsUnhandledExceptionRuntimeFixUpWorks ([Values] AndroidRuntime runtime) + { + const bool isRelease = true; + if (IgnoreUnsupportedConfiguration (runtime, release: isRelease)) { + return; + } + + switch (runtime) { + case AndroidRuntime.NativeAOT: + Assert.Ignore ("NativeAOT does not support marshal methods"); + break; + + case AndroidRuntime.CoreCLR: + Assert.Ignore ("CoreCLR currently doesn't work due to a bug in Mono.Cecil"); + break; + } + + var proj = new XamarinAndroidApplicationProject { + IsRelease = isRelease, + EnableMarshalMethods = true, + }; + proj.SetRuntime (runtime); + using var builder = CreateApkBuilder (); + Assert.IsTrue (builder.Build (proj), "Build should have succeeded."); + + string monoAndroidRuntimePath = Path.Combine ( + Root, + builder.ProjectDirectory, + proj.IntermediateOutputPath, + "android-arm64", + "linked", + "Mono.Android.Runtime.dll" + ); + FileAssert.Exists (monoAndroidRuntimePath); + + using var asm = AssemblyDefinition.ReadAssembly (monoAndroidRuntimePath); + const string TypeName = "Android.Runtime.AndroidEnvironmentInternal"; + TypeDefinition? type = null; + + foreach (ModuleDefinition module in asm.Modules) { + foreach (TypeDefinition t in module.Types) { + if (t.FullName.Equals (TypeName, StringComparison.Ordinal)) { + type = t; + break; + } + } + } + + Assert.NotNull (type, $"Failed to find the '{TypeName}' type in '{monoAndroidRuntimePath}'"); + Assert.IsTrue (type.IsPublic, $"Type '{TypeName}' should be public"); + + // Additionally verify that the UnhandledException method is also public + const string MethodName = "UnhandledException"; + MethodDefinition? method = null; + foreach (MethodDefinition m in type.Methods) { + if (m.Name.Equals (MethodName, StringComparison.Ordinal)) { + method = m; + break; + } + } + + Assert.NotNull (method, $"Failed to find the '{MethodName}' method in type '{TypeName}'"); + Assert.IsTrue (method.IsPublic, $"Method '{MethodName}' should be public"); + } } }