diff --git a/external/Java.Interop b/external/Java.Interop index 70493645c7d..8d544738ad2 160000 --- a/external/Java.Interop +++ b/external/Java.Interop @@ -1 +1 @@ -Subproject commit 70493645c7d95648010a4cef948234a28744c03f +Subproject commit 8d544738ad294b4faf13d189eeeb02f0313e00b3 diff --git a/src/Microsoft.Android.Runtime.NativeAOT/Java.Interop/JreRuntime.cs b/src/Microsoft.Android.Runtime.NativeAOT/Java.Interop/JreRuntime.cs index 5891149578f..9c2909e7c74 100644 --- a/src/Microsoft.Android.Runtime.NativeAOT/Java.Interop/JreRuntime.cs +++ b/src/Microsoft.Android.Runtime.NativeAOT/Java.Interop/JreRuntime.cs @@ -61,7 +61,7 @@ static NativeAotRuntimeOptions CreateJreVM (NativeAotRuntimeOptions builder) builder.TypeManager ??= CreateDefaultTypeManager (); #endif // NET - builder.ValueManager ??= new JavaMarshalValueManager (); + builder.ValueManager ??= CreateDefaultValueManager (); builder.ObjectReferenceManager ??= new Android.Runtime.AndroidObjectReferenceManager (); if (builder.InvocationPointer != IntPtr.Zero || builder.EnvironmentPointer != IntPtr.Zero) @@ -84,6 +84,13 @@ static JniRuntime.JniTypeManager CreateDefaultTypeManager () return new ManagedTypeManager (); } + [UnconditionalSuppressMessage ("Trimming", "IL2026", Justification = "CoreCLR value manager is preserved by the MarkJavaObjects trimmer step.")] + [UnconditionalSuppressMessage ("Trimming", "IL3050", Justification = "This value manager won't be used in Native AOT builds in the future.")] + static JniRuntime.JniValueManager CreateDefaultValueManager () + { + return new JavaMarshalValueManager (); + } + public override string? GetCurrentManagedThreadName () { return Thread.CurrentThread.Name; diff --git a/src/Mono.Android/Android.App/IntentFilterAttribute.Partial.cs b/src/Mono.Android/Android.App/IntentFilterAttribute.Partial.cs index 5bb6dfdcdf2..ad9cd0b90d8 100644 --- a/src/Mono.Android/Android.App/IntentFilterAttribute.Partial.cs +++ b/src/Mono.Android/Android.App/IntentFilterAttribute.Partial.cs @@ -1,4 +1,4 @@ -#nullable disable +#nullable enable using System; diff --git a/src/Mono.Android/Android.Graphics/Color.cs b/src/Mono.Android/Android.Graphics/Color.cs index 1f4bde8455c..317a3b59e73 100644 --- a/src/Mono.Android/Android.Graphics/Color.cs +++ b/src/Mono.Android/Android.Graphics/Color.cs @@ -434,18 +434,13 @@ public static void RGBToHSV (int red, int green, int blue, float[] hsv) public class ColorValueMarshaler : JniValueMarshaler { - const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors; const string ExpressionRequiresUnreferencedCode = "System.Linq.Expression usage may trim away required code."; public override Type MarshalType { get { return typeof (int); } } - public override Color CreateGenericValue ( - ref JniObjectReference reference, - JniObjectReferenceOptions options, - [DynamicallyAccessedMembers (Constructors)] - Type targetType) + public override Color CreateGenericValue (ref JniObjectReference reference, JniObjectReferenceOptions options, Type targetType) { throw new NotImplementedException (); } diff --git a/src/Mono.Android/Android.Runtime/AndroidRuntime.cs b/src/Mono.Android/Android.Runtime/AndroidRuntime.cs index dd0d94a73a9..7f5507cb396 100644 --- a/src/Mono.Android/Android.Runtime/AndroidRuntime.cs +++ b/src/Mono.Android/Android.Runtime/AndroidRuntime.cs @@ -314,11 +314,6 @@ public override void DeleteWeakGlobalReference (ref JniObjectReference value) class AndroidTypeManager : JniRuntime.ReflectionJniTypeManager { bool jniAddNativeMethodRegistrationAttributePresent; - const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors; - const DynamicallyAccessedMemberTypes Methods = DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods; - const DynamicallyAccessedMemberTypes MethodsAndPrivateNested = Methods | DynamicallyAccessedMemberTypes.NonPublicNestedTypes; - const DynamicallyAccessedMemberTypes MethodsConstructors = MethodsAndPrivateNested | Constructors; - public AndroidTypeManager (bool jniAddNativeMethodRegistrationAttributePresent) { this.jniAddNativeMethodRegistrationAttributePresent = jniAddNativeMethodRegistrationAttributePresent; @@ -335,7 +330,6 @@ protected override IEnumerable GetTypesForSimpleReference (string jniSimpl } [UnconditionalSuppressMessage ("Trimming", "IL2073", Justification = "Temporary suppression until legacy typemap entries carry DAM annotations.")] - [return: DynamicallyAccessedMembers (MethodsConstructors)] protected override Type? GetTypeForSimpleReference (string jniSimpleReference) { var type = base.GetTypeForSimpleReference (jniSimpleReference); @@ -384,10 +378,7 @@ protected override IEnumerable GetSimpleReferences (Type type) return JniRemappingLookup.GetReplacementMethodInfo (jniSourceType, jniMethodName, jniMethodSignature); } - [return: DynamicallyAccessedMembers (Constructors)] - protected override Type? GetInvokerTypeCore ( - [DynamicallyAccessedMembers (Constructors)] - Type type) + protected override Type? GetInvokerTypeCore (Type type) { if (type.IsInterface || type.IsAbstract) { return JavaObjectExtensions.GetInvokerType (type) @@ -500,17 +491,17 @@ static bool CallRegisterMethodByIndex (JniNativeMethodRegistrationArguments argu [Obsolete ("Use RegisterNativeMembers(JniType, Type, ReadOnlySpan) instead.")] public override void RegisterNativeMembers ( JniType nativeClass, - [DynamicallyAccessedMembers (MethodsAndPrivateNested)] Type type, string? methods) => RegisterNativeMembers (nativeClass, type, methods.AsSpan ()); [UnconditionalSuppressMessage ("Trimming", "IL2057", Justification = "Type.GetType() can never statically know the string value parsed from parameter 'methods'.")] [UnconditionalSuppressMessage ("Trimming", "IL2067", Justification = "Delegate.CreateDelegate() can never statically know the string value parsed from parameter 'methods'.")] + [UnconditionalSuppressMessage ("Trimming", "IL2070", Justification = "GetMethods can never statically know the string value parsed from parameter 'methods'.")] [UnconditionalSuppressMessage ("Trimming", "IL2072", Justification = "Delegate.CreateDelegate() can never statically know the string value parsed from parameter 'methods'.")] public override void RegisterNativeMembers ( JniType nativeClass, - [DynamicallyAccessedMembers (MethodsAndPrivateNested)] Type type, + Type type, ReadOnlySpan methods) { try { @@ -852,7 +843,7 @@ internal void RemovePeer (IJavaPeerable value, IntPtr hash) public override void ActivatePeer ( JniObjectReference reference, - [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] Type type, + Type type, ConstructorInfo cinfo, object?[]? argumentValues) { diff --git a/src/Mono.Android/Android.Runtime/IJavaObjectValueMarshaler.cs b/src/Mono.Android/Android.Runtime/IJavaObjectValueMarshaler.cs index 79230b9d2eb..bd7d8942936 100644 --- a/src/Mono.Android/Android.Runtime/IJavaObjectValueMarshaler.cs +++ b/src/Mono.Android/Android.Runtime/IJavaObjectValueMarshaler.cs @@ -9,8 +9,6 @@ namespace Android.Runtime { sealed class IJavaObjectValueMarshaler : JniValueMarshaler { - - const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors; const string ExpressionRequiresUnreferencedCode = "System.Linq.Expression usage may trim away required code."; internal static IJavaObjectValueMarshaler Instance = new IJavaObjectValueMarshaler (); @@ -18,7 +16,6 @@ sealed class IJavaObjectValueMarshaler : JniValueMarshaler { public override IJavaObject CreateGenericValue ( ref JniObjectReference reference, JniObjectReferenceOptions options, - [DynamicallyAccessedMembers (Constructors)] Type? targetType) { throw new NotImplementedException (); diff --git a/src/Mono.Android/Android.Runtime/JNIEnvInit.cs b/src/Mono.Android/Android.Runtime/JNIEnvInit.cs index c2e7ea913ca..ce424298645 100644 --- a/src/Mono.Android/Android.Runtime/JNIEnvInit.cs +++ b/src/Mono.Android/Android.Runtime/JNIEnvInit.cs @@ -188,14 +188,21 @@ internal static JniRuntime.JniValueManager CreateValueManager () } if (RuntimeFeature.IsCoreClrRuntime) { - return new JavaMarshalValueManager (); + return CreateJavaMarshalValueManager (); } if (RuntimeFeature.IsNativeAotRuntime) { - return new JavaMarshalValueManager (); + return CreateJavaMarshalValueManager (); } throw new NotSupportedException ("Internal error: unknown runtime not supported"); + + [UnconditionalSuppressMessage ("Trimming", "IL2026", Justification = "CoreCLR value manager is preserved by the MarkJavaObjects trimmer step.")] + [UnconditionalSuppressMessage ("Trimming", "IL3050", Justification = "This value manager won't be used in Native AOT builds in the future.")] + JniRuntime.JniValueManager CreateJavaMarshalValueManager () + { + return new JavaMarshalValueManager (); + } } static void InitializeCommonState (JnienvInitializeArgs args) diff --git a/src/Mono.Android/Microsoft.Android.Runtime/AndroidReflectionJniValueManager.cs b/src/Mono.Android/Microsoft.Android.Runtime/AndroidReflectionJniValueManager.cs deleted file mode 100644 index 1ead336ccb6..00000000000 --- a/src/Mono.Android/Microsoft.Android.Runtime/AndroidReflectionJniValueManager.cs +++ /dev/null @@ -1,176 +0,0 @@ -using System; -using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.Reflection; -using System.Runtime.CompilerServices; -using Java.Interop; - -namespace Microsoft.Android.Runtime; - -[UnconditionalSuppressMessage ("Trimming", "IL2026", Justification = "Temporary suppression for Java.Interop reflection manager base.")] -abstract class AndroidReflectionJniValueManager : JniRuntime.ReflectionJniValueManager -{ - const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors; - - public override IJavaPeerable? CreatePeer ( - ref JniObjectReference reference, - JniObjectReferenceOptions transfer, - [DynamicallyAccessedMembers (Constructors)] - Type? targetType) - { - EnsureNotDisposed (); - - if (!reference.IsValid) { - return null; - } - - targetType = targetType ?? typeof (global::Java.Interop.JavaObject); - targetType = GetPeerType (targetType); - - if (!typeof (IJavaPeerable).IsAssignableFrom (targetType)) { - throw new ArgumentException ($"targetType `{targetType.AssemblyQualifiedName}` must implement IJavaPeerable!", nameof (targetType)); - } - - var targetSig = Runtime.TypeManager.GetTypeSignature (targetType); - if (!targetSig.IsValid || targetSig.SimpleReference == null) { - throw new ArgumentException ($"Could not determine Java type corresponding to `{targetType.AssemblyQualifiedName}`.", nameof (targetType)); - } - - var refClass = JniEnvironment.Types.GetObjectClass (reference); - JniObjectReference targetClass; - try { - targetClass = JniEnvironment.Types.FindClass (targetSig.SimpleReference); - } catch (Exception e) { - JniObjectReference.Dispose (ref refClass); - throw new ArgumentException ($"Could not find Java class `{targetSig.SimpleReference}`.", - nameof (targetType), - e); - } - - if (!JniEnvironment.Types.IsAssignableFrom (refClass, targetClass)) { - JniObjectReference.Dispose (ref refClass); - JniObjectReference.Dispose (ref targetClass); - return null; - } - - JniObjectReference.Dispose (ref targetClass); - - var peer = CreatePeerInstance (ref refClass, targetType, ref reference, transfer); - if (peer == null) { - throw new NotSupportedException (string.Format (CultureInfo.InvariantCulture, "Could not find an appropriate constructable wrapper type for Java type '{0}', targetType='{1}'.", - JniEnvironment.Types.GetJniTypeNameFromInstance (reference), targetType)); - } - peer.SetJniManagedPeerState (peer.JniManagedPeerState | JniManagedPeerStates.Replaceable); - return peer; - } - - [return: DynamicallyAccessedMembers (Constructors)] - static Type GetPeerType ([DynamicallyAccessedMembers (Constructors)] Type type) - { - if (type == typeof (object)) { - return typeof (global::Java.Interop.JavaObject); - } - if (type == typeof (IJavaPeerable)) { - return typeof (global::Java.Interop.JavaObject); - } - if (type == typeof (Exception)) { - return typeof (JavaException); - } - return type; - } - - IJavaPeerable? CreatePeerInstance ( - ref JniObjectReference klass, - [DynamicallyAccessedMembers (Constructors)] - Type targetType, - ref JniObjectReference reference, - JniObjectReferenceOptions transfer) - { - var jniTypeName = JniEnvironment.Types.GetJniTypeNameFromClass (klass); - - while (jniTypeName != null) { - JniTypeSignature sig; - if (!JniTypeSignature.TryParse (jniTypeName, out sig)) { - return null; - } - - Type? type = GetTypeAssignableTo (sig, targetType); - if (type != null) { - var peer = TryCreatePeerInstance (ref reference, transfer, type); - - if (peer != null) { - JniObjectReference.Dispose (ref klass); - return peer; - } - } - - var super = JniEnvironment.Types.GetSuperclass (klass); - jniTypeName = super.IsValid - ? JniEnvironment.Types.GetJniTypeNameFromClass (super) - : null; - - JniObjectReference.Dispose (ref klass, JniObjectReferenceOptions.CopyAndDispose); - klass = super; - } - JniObjectReference.Dispose (ref klass, JniObjectReferenceOptions.CopyAndDispose); - - return TryCreatePeerInstance (ref reference, transfer, targetType); - - [return: DynamicallyAccessedMembers (Constructors)] - Type? GetTypeAssignableTo (JniTypeSignature sig, Type targetType) - { - foreach (var t in Runtime.TypeManager.GetReflectionConstructibleTypes (sig)) { - if (targetType.IsAssignableFrom (t.Type)) { - return t.Type; - } - } - return null; - } - } - - IJavaPeerable? TryCreatePeerInstance ( - ref JniObjectReference reference, - JniObjectReferenceOptions options, - [DynamicallyAccessedMembers (Constructors)] - Type type) - { - type = Runtime.TypeManager.GetInvokerType (type) ?? type; - - var self = (IJavaPeerable) RuntimeHelpers.GetUninitializedObject (type); - self.SetJniManagedPeerState (JniManagedPeerStates.Replaceable | JniManagedPeerStates.Activatable); - - var constructed = false; - try { - constructed = TryConstructPeer (self, ref reference, options, type); - } finally { - if (!constructed) { - GC.SuppressFinalize (self); - self = null; - } - } - return self; - } - - static readonly Type ByRefJniObjectReference = typeof (JniObjectReference).MakeByRefType (); - static readonly Type[] JIConstructorSignature = new Type [] { ByRefJniObjectReference, typeof (JniObjectReferenceOptions) }; - - protected virtual bool TryConstructPeer ( - IJavaPeerable self, - ref JniObjectReference reference, - JniObjectReferenceOptions options, - [DynamicallyAccessedMembers (Constructors)] - Type type) - { - var c = type.GetConstructor (BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, JIConstructorSignature, null); - if (c != null) { - var args = new object[] { - reference, - options, - }; - c.Invoke (self, args); - reference = (JniObjectReference) args [0]; - return true; - } - return false; - } -} diff --git a/src/Mono.Android/Microsoft.Android.Runtime/JavaMarshalValueManager.cs b/src/Mono.Android/Microsoft.Android.Runtime/JavaMarshalValueManager.cs index 1ae56e7ce86..a584a991e0d 100644 --- a/src/Mono.Android/Microsoft.Android.Runtime/JavaMarshalValueManager.cs +++ b/src/Mono.Android/Microsoft.Android.Runtime/JavaMarshalValueManager.cs @@ -7,32 +7,20 @@ namespace Microsoft.Android.Runtime; -class JavaMarshalValueManager : AndroidReflectionJniValueManager +[RequiresDynamicCode ("This value manager is reflection-backed and can break in AOT scenarios.")] +[RequiresUnreferencedCode ("This value manager is reflection-backed and relies on custom trimming rules.")] +sealed class JavaMarshalValueManager : JniRuntime.ReflectionJniValueManager { const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors; const BindingFlags ActivationConstructorBindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; static readonly Type[] XAConstructorSignature = new Type [] { typeof (IntPtr), typeof (JniHandleOwnership) }; - bool disposed; - public JavaMarshalValueManager () { JavaMarshalRegisteredPeers.InitializeIfNeeded (); } - protected override void Dispose (bool disposing) - { - disposed = true; - base.Dispose (disposing); - } - - void ThrowIfDisposed () - { - if (disposed) - throw new ObjectDisposedException (nameof (JavaMarshalValueManager)); - } - public override void WaitForGCBridgeProcessing () { // Intentionally empty. The Mono runtime's own implementation acknowledges this @@ -68,7 +56,7 @@ public override void FinalizePeer (IJavaPeerable value) JavaMarshalRegisteredPeers.FinalizePeer (value); } - public override void ActivatePeer (JniObjectReference reference, [DynamicallyAccessedMembers (Constructors)] Type type, ConstructorInfo cinfo, object?[]? argumentValues) + public override void ActivatePeer (JniObjectReference reference, Type type, ConstructorInfo cinfo, object?[]? argumentValues) { if (RuntimeFeature.TrimmableTypeMap) throw new PlatformNotSupportedException ("Activating Java peers is not supported when TrimmableTypeMap is enabled."); @@ -87,7 +75,7 @@ public override List GetSurfacedPeers () [DynamicallyAccessedMembers (Constructors)] Type? targetType) { - ThrowIfDisposed (); + EnsureNotDisposed (); if (!reference.IsValid) { return null; diff --git a/src/Mono.Android/Microsoft.Android.Runtime/ManagedTypeManager.cs b/src/Mono.Android/Microsoft.Android.Runtime/ManagedTypeManager.cs index aaee4516788..db254b65bb8 100644 --- a/src/Mono.Android/Microsoft.Android.Runtime/ManagedTypeManager.cs +++ b/src/Mono.Android/Microsoft.Android.Runtime/ManagedTypeManager.cs @@ -19,46 +19,26 @@ public ManagedTypeManager () { } - [return: DynamicallyAccessedMembers (Constructors)] - protected override Type? GetInvokerTypeCore ( - [DynamicallyAccessedMembers (Constructors)] - Type type) + [UnconditionalSuppressMessage ("Trimming", "IL2026", Justification = "'Invoker' types are preserved by the MarkJavaObjects trimmer step.")] + [UnconditionalSuppressMessage ("Trimming", "IL2055", Justification = "'Invoker' types are preserved by the MarkJavaObjects trimmer step.")] + [UnconditionalSuppressMessage ("Trimming", "IL2073", Justification = "Generic 'Invoker' types are preserved by the MarkJavaObjects trimmer step.")] + [UnconditionalSuppressMessage ("AOT", "IL3050", Justification = "Generic 'Invoker' types may not be available in AOT scenarios.")] + protected override Type? GetInvokerTypeCore (Type type) { const string suffix = "Invoker"; - // https://github.com/xamarin/xamarin-android/blob/5472eec991cc075e4b0c09cd98a2331fb93aa0f3/src/Microsoft.Android.Sdk.ILLink/MarkJavaObjects.cs#L176-L186 - const string assemblyGetTypeMessage = "'Invoker' types are preserved by the MarkJavaObjects trimmer step."; - const string makeGenericTypeMessage = "Generic 'Invoker' types are preserved by the MarkJavaObjects trimmer step."; - - [UnconditionalSuppressMessage ("Trimming", "IL2026", Justification = assemblyGetTypeMessage)] - [UnconditionalSuppressMessage ("Trimming", "IL2073", Justification = assemblyGetTypeMessage)] - [return: DynamicallyAccessedMembers (Constructors)] - static Type? AssemblyGetType (Assembly assembly, string typeName) => - assembly.GetType (typeName); - - [UnconditionalSuppressMessage ("Trimming", "IL2055", Justification = makeGenericTypeMessage)] - [return: DynamicallyAccessedMembers (Constructors)] - static Type MakeGenericType ( - [DynamicallyAccessedMembers (Constructors)] - Type type, - Type [] arguments) => - // FIXME: https://github.com/dotnet/java-interop/issues/1192 - #pragma warning disable IL3050 - type.MakeGenericType (arguments); - #pragma warning restore IL3050 - Type[] arguments = type.GetGenericArguments (); if (arguments.Length == 0) - return AssemblyGetType (type.Assembly, type + suffix) ?? base.GetInvokerTypeCore (type); + return type.Assembly.GetType (type + suffix) ?? base.GetInvokerTypeCore (type); Type definition = type.GetGenericTypeDefinition (); int bt = definition.FullName!.IndexOf ("`", StringComparison.Ordinal); if (bt == -1) throw new NotSupportedException ("Generic type doesn't follow generic type naming convention! " + type.FullName); - Type? suffixDefinition = AssemblyGetType (definition.Assembly, + Type? suffixDefinition = definition.Assembly.GetType ( definition.FullName.Substring (0, bt) + suffix + definition.FullName.Substring (bt)); if (suffixDefinition == null) return base.GetInvokerTypeCore (type); - return MakeGenericType (suffixDefinition, arguments); + return suffixDefinition.MakeGenericType (arguments); } // NOTE: suppressions below also in `src/Mono.Android/Android.Runtime/AndroidRuntime.cs` @@ -67,7 +47,6 @@ static Type MakeGenericType ( [UnconditionalSuppressMessage ("Trimming", "IL2072", Justification = "Delegate.CreateDelegate() can never statically know the string value parsed from parameter 'methods'.")] public override void RegisterNativeMembers ( JniType nativeClass, - [DynamicallyAccessedMembers (MethodsAndPrivateNested)] Type type, ReadOnlySpan methods) { @@ -144,7 +123,6 @@ protected override IEnumerable GetTypesForSimpleReference (string jniSimpl } [UnconditionalSuppressMessage ("Trimming", "IL2068", Justification = "Temporary suppression until ManagedTypeMapping type entries carry DAM annotations.")] - [return: DynamicallyAccessedMembers (MethodsConstructors)] protected override Type? GetTypeForSimpleReference (string jniSimpleReference) { var type = base.GetTypeForSimpleReference (jniSimpleReference); diff --git a/src/Mono.Android/Microsoft.Android.Runtime/SimpleValueManager.cs b/src/Mono.Android/Microsoft.Android.Runtime/SimpleValueManager.cs deleted file mode 100644 index 0546f43d378..00000000000 --- a/src/Mono.Android/Microsoft.Android.Runtime/SimpleValueManager.cs +++ /dev/null @@ -1,259 +0,0 @@ -// Originally from: https://github.com/dotnet/java-interop/blob/9b1d8781e8e322849d05efac32119c913b21c192/src/Java.Runtime.Environment/Java.Interop/ManagedValueManager.cs -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Threading; -using Android.Runtime; -using Java.Interop; - -namespace Microsoft.Android.Runtime; - -class SimpleValueManager : AndroidReflectionJniValueManager -{ - const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors; - - Dictionary>? RegisteredInstances = new Dictionary>(); - - internal SimpleValueManager () - { - } - - public override void WaitForGCBridgeProcessing () - { - } - - public override void CollectPeers () - { - if (RegisteredInstances == null) - throw new ObjectDisposedException (nameof (SimpleValueManager)); - - var peers = new List (); - - lock (RegisteredInstances) { - foreach (var ps in RegisteredInstances.Values) { - foreach (var p in ps) { - peers.Add (p); - } - } - RegisteredInstances.Clear (); - } - List? exceptions = null; - foreach (var peer in peers) { - try { - peer.Dispose (); - } - catch (Exception e) { - exceptions = exceptions ?? new List (); - exceptions.Add (e); - } - } - if (exceptions != null) - throw new AggregateException ("Exceptions while collecting peers.", exceptions); - } - - public override void AddPeer (IJavaPeerable value) - { - if (RegisteredInstances == null) - throw new ObjectDisposedException (nameof (SimpleValueManager)); - - var r = value.PeerReference; - if (!r.IsValid) - throw new ObjectDisposedException (value.GetType ().FullName); - - if (r.Type != JniObjectReferenceType.Global) { - value.SetPeerReference (r.NewGlobalRef ()); - JniObjectReference.Dispose (ref r, JniObjectReferenceOptions.CopyAndDispose); - } - int key = value.JniIdentityHashCode; - lock (RegisteredInstances) { - List? peers; - if (!RegisteredInstances.TryGetValue (key, out peers)) { - peers = new List () { - value, - }; - RegisteredInstances.Add (key, peers); - return; - } - - for (int i = peers.Count - 1; i >= 0; i--) { - var p = peers [i]; - if (!JniEnvironment.Types.IsSameObject (p.PeerReference, value.PeerReference)) - continue; - if (Replaceable (p)) { - peers [i] = value; - } else { - WarnNotReplacing (key, value, p); - } - return; - } - peers.Add (value); - } - } - - static bool Replaceable (IJavaPeerable peer) - { - if (peer == null) - return true; - return peer.JniManagedPeerState.HasFlag (JniManagedPeerStates.Replaceable); - } - - void WarnNotReplacing (int key, IJavaPeerable ignoreValue, IJavaPeerable keepValue) - { - Runtime.ObjectReferenceManager.WriteGlobalReferenceLine ( - "Warning: Not registering PeerReference={0} IdentityHashCode=0x{1} Instance={2} Instance.Type={3} Java.Type={4}; " + - "keeping previously registered PeerReference={5} Instance={6} Instance.Type={7} Java.Type={8}.", - ignoreValue.PeerReference.ToString (), - key.ToString ("x", CultureInfo.InvariantCulture), - RuntimeHelpers.GetHashCode (ignoreValue).ToString ("x", CultureInfo.InvariantCulture), - ignoreValue.GetType ().FullName, - JniEnvironment.Types.GetJniTypeNameFromInstance (ignoreValue.PeerReference), - keepValue.PeerReference.ToString (), - RuntimeHelpers.GetHashCode (keepValue).ToString ("x", CultureInfo.InvariantCulture), - keepValue.GetType ().FullName, - JniEnvironment.Types.GetJniTypeNameFromInstance (keepValue.PeerReference)); - } - - public override IJavaPeerable? PeekPeer (JniObjectReference reference) - { - if (RegisteredInstances == null) - throw new ObjectDisposedException (nameof (SimpleValueManager)); - - if (!reference.IsValid) - return null; - - int key = GetJniIdentityHashCode (reference); - - lock (RegisteredInstances) { - List? peers; - if (!RegisteredInstances.TryGetValue (key, out peers)) - return null; - - for (int i = peers.Count - 1; i >= 0; i--) { - var p = peers [i]; - if (JniEnvironment.Types.IsSameObject (reference, p.PeerReference)) - return p; - } - if (peers.Count == 0) - RegisteredInstances.Remove (key); - } - return null; - } - - public override void RemovePeer (IJavaPeerable value) - { - if (RegisteredInstances == null) - throw new ObjectDisposedException (nameof (SimpleValueManager)); - - if (value == null) - throw new ArgumentNullException (nameof (value)); - - int key = value.JniIdentityHashCode; - lock (RegisteredInstances) { - List? peers; - if (!RegisteredInstances.TryGetValue (key, out peers)) - return; - - for (int i = peers.Count - 1; i >= 0; i--) { - var p = peers [i]; - if (object.ReferenceEquals (value, p)) { - peers.RemoveAt (i); - } - } - if (peers.Count == 0) - RegisteredInstances.Remove (key); - } - } - - public override void FinalizePeer (IJavaPeerable value) - { - var h = value.PeerReference; - var o = Runtime.ObjectReferenceManager; - // MUST NOT use SafeHandle.ReferenceType: local refs are tied to a JniEnvironment - // and the JniEnvironment's corresponding thread; it's a thread-local value. - // Accessing SafeHandle.ReferenceType won't kill anything (so far...), but - // instead it always returns JniReferenceType.Invalid. - if (!h.IsValid || h.Type == JniObjectReferenceType.Local) { - if (o.LogGlobalReferenceMessages) { - o.WriteGlobalReferenceLine ("Finalizing PeerReference={0} IdentityHashCode=0x{1} Instance=0x{2} Instance.Type={3}", - h.ToString (), - value.JniIdentityHashCode.ToString ("x", CultureInfo.InvariantCulture), - RuntimeHelpers.GetHashCode (value).ToString ("x", CultureInfo.InvariantCulture), - value.GetType ().ToString ()); - } - RemovePeer (value); - value.SetPeerReference (new JniObjectReference ()); - value.Finalized (); - return; - } - - RemovePeer (value); - if (o.LogGlobalReferenceMessages) { - o.WriteGlobalReferenceLine ("Finalizing PeerReference={0} IdentityHashCode=0x{1} Instance=0x{2} Instance.Type={3}", - h.ToString (), - value.JniIdentityHashCode.ToString ("x", CultureInfo.InvariantCulture), - RuntimeHelpers.GetHashCode (value).ToString ("x", CultureInfo.InvariantCulture), - value.GetType ().ToString ()); - } - value.SetPeerReference (new JniObjectReference ()); - JniObjectReference.Dispose (ref h); - value.Finalized (); - } - - public override List GetSurfacedPeers () - { - if (RegisteredInstances == null) - throw new ObjectDisposedException (nameof (SimpleValueManager)); - - lock (RegisteredInstances) { - var peers = new List (RegisteredInstances.Count); - foreach (var e in RegisteredInstances) { - foreach (var p in e.Value) { - peers.Add (new JniSurfacedPeerInfo (e.Key, new WeakReference (p))); - } - } - return peers; - } - } - - const BindingFlags ActivationConstructorBindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; - - static readonly Type[] XAConstructorSignature = new Type [] { typeof (IntPtr), typeof (JniHandleOwnership) }; - - protected override bool TryConstructPeer ( - IJavaPeerable self, - ref JniObjectReference reference, - JniObjectReferenceOptions options, - [DynamicallyAccessedMembers (Constructors)] - Type type) - { - var c = type.GetConstructor (ActivationConstructorBindingFlags, null, XAConstructorSignature, null); - if (c != null) { - var args = new object[] { - reference.Handle, - JniHandleOwnership.DoNotTransfer, - }; - c.Invoke (self, args); - JniObjectReference.Dispose (ref reference, options); - return true; - } - return base.TryConstructPeer (self, ref reference, options, type); - } - - protected override bool TryUnboxPeerObject (IJavaPeerable value, [NotNullWhen (true)]out object? result) - { - var proxy = value as JavaProxyThrowable; - if (proxy != null) { - result = proxy.InnerException; - return true; - } - return base.TryUnboxPeerObject (value, out result); - } -} diff --git a/src/Mono.Android/Microsoft.Android.Runtime/TrimmableTypeMapTypeManager.cs b/src/Mono.Android/Microsoft.Android.Runtime/TrimmableTypeMapTypeManager.cs index c2514055757..5ff5f6d4a9d 100644 --- a/src/Mono.Android/Microsoft.Android.Runtime/TrimmableTypeMapTypeManager.cs +++ b/src/Mono.Android/Microsoft.Android.Runtime/TrimmableTypeMapTypeManager.cs @@ -18,10 +18,6 @@ namespace Microsoft.Android.Runtime; class TrimmableTypeMapTypeManager : JniRuntime.ReflectionJniTypeManager { const string NoSimpleReference = "\0"; - const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors; - const DynamicallyAccessedMemberTypes Methods = DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods; - const DynamicallyAccessedMemberTypes MethodsAndPrivateNested = Methods | DynamicallyAccessedMemberTypes.NonPublicNestedTypes; - const DynamicallyAccessedMemberTypes MethodsConstructors = MethodsAndPrivateNested | Constructors; readonly ConcurrentDictionary _simpleReferenceCache = new (); protected override IEnumerable GetTypesForSimpleReference (string jniSimpleReference) @@ -37,8 +33,6 @@ protected override IEnumerable GetTypesForSimpleReference (string jniSimpl } } - [UnconditionalSuppressMessage ("Trimming", "IL2063", Justification = "Temporary suppression until trimmable typemap type entries carry DAM annotations.")] - [return: DynamicallyAccessedMembers (MethodsConstructors)] protected override Type? GetTypeForSimpleReference (string jniSimpleReference) { var type = base.GetTypeForSimpleReference (jniSimpleReference); @@ -100,10 +94,7 @@ protected override IEnumerable GetSimpleReferences (Type type) } } - [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] - protected override Type? GetInvokerTypeCore ( - [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] - Type type) + protected override Type? GetInvokerTypeCore (Type type) { var invokerType = TrimmableTypeMap.Instance.GetInvokerType (type); if (invokerType != null) { @@ -130,7 +121,6 @@ protected override IEnumerable GetSimpleReferences (Type type) public override void RegisterNativeMembers ( JniType nativeClass, - [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods | DynamicallyAccessedMemberTypes.NonPublicNestedTypes)] Type type, ReadOnlySpan methods) { diff --git a/src/Mono.Android/Mono.Android.csproj b/src/Mono.Android/Mono.Android.csproj index 75b728af514..9b90b93ce48 100644 --- a/src/Mono.Android/Mono.Android.csproj +++ b/src/Mono.Android/Mono.Android.csproj @@ -363,14 +363,12 @@ - - 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 420fe81c8f7..4f3cfb71bbb 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 @@ -482,11 +482,15 @@ public void BuildHasNoWarnings (bool isRelease, bool multidex, string packageFor Assert.IsTrue (b.Build (proj), "Build should have succeeded."); if (runtime == AndroidRuntime.NativeAOT) { - // NativeAOT currently (June 2026) produces 8 `ILC : AOT analysis warning IL3050` warnings for various - // bits of code. Even though this test expects no warnings and the above likely make the app not work - // correctly at run time, it is still worth running this test under NativeAOT to test for the absence - // of other warnings. - int numberOfExpectedWarnings = 8; + // NativeAOT currently (Jun 2026) produces 4 `ILC : AOT analysis warning IL3050` + // warnings: two distinct warnings (the reflection-backed ManagedTypeManager + // generic ctor and JNINativeWrapper.CreateDelegate), each surfaced twice in the + // MSBuild summary (once per publish target context). #11753 replaced the JNIEnv + // array path with JavaArrayProxy, removing the previous JNIEnv.MakeArrayType + // warning. Even though this test expects no warnings and the above likely make + // the app not work correctly at run time, it is still worth running this test + // under NativeAOT to test for the absence of other warnings. + int numberOfExpectedWarnings = 4; // MSBuild prints a " N Warning(s)" summary line near the end of the build; parse N so the // assertion can report the actual count instead of a bare "Expected: True But was: False". diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.CoreCLR.apkdesc b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.CoreCLR.apkdesc index 0d3d9720ca1..a00fd898add 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.CoreCLR.apkdesc +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.CoreCLR.apkdesc @@ -8,16 +8,16 @@ "Size": 402352 }, "lib/arm64-v8a/libassembly-store.so": { - "Size": 3461344 + "Size": 3229120 }, "lib/arm64-v8a/libclrjit.so": { - "Size": 2804464 + "Size": 2835128 }, "lib/arm64-v8a/libcoreclr.so": { - "Size": 4872088 + "Size": 4891056 }, "lib/arm64-v8a/libmonodroid.so": { - "Size": 1325808 + "Size": 1324320 }, "lib/arm64-v8a/libSystem.Globalization.Native.so": { "Size": 72112 @@ -32,7 +32,7 @@ "Size": 168080 }, "lib/arm64-v8a/libxamarin-app.so": { - "Size": 20776 + "Size": 20984 }, "res/drawable-hdpi-v4/icon.png": { "Size": 2178 @@ -59,5 +59,5 @@ "Size": 1904 } }, - "PackageSize": 7497147 + "PackageSize": 7312827 } \ No newline at end of file diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.NativeAOT.apkdesc b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.NativeAOT.apkdesc index 79351723c31..d19175aaf76 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.NativeAOT.apkdesc +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.NativeAOT.apkdesc @@ -8,7 +8,7 @@ "Size": 22016 }, "lib/arm64-v8a/libUnnamedProject.so": { - "Size": 5999448 + "Size": 5411456 }, "res/drawable-hdpi-v4/icon.png": { "Size": 2178 @@ -35,5 +35,5 @@ "Size": 1904 } }, - "PackageSize": 2474779 + "PackageSize": 2286363 } \ No newline at end of file diff --git a/tests/api-compatibility/acceptable-breakages-vReference-net11.0.txt b/tests/api-compatibility/acceptable-breakages-vReference-net11.0.txt index 0feba3a1457..05022f14024 100644 --- a/tests/api-compatibility/acceptable-breakages-vReference-net11.0.txt +++ b/tests/api-compatibility/acceptable-breakages-vReference-net11.0.txt @@ -1,5 +1,4 @@ Compat issues with assembly Mono.Android: -CannotChangeAttribute : Attribute 'System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute' on parameter 'targetType' on member 'Android.Graphics.ColorValueMarshaler.CreateGenericValue(Java.Interop.JniObjectReference, Java.Interop.JniObjectReferenceOptions, System.Type)' changed from '[DynamicallyAccessedMembersAttribute(8199)]' in the contract to '[DynamicallyAccessedMembersAttribute(7)]' in the implementation. CannotChangeAttribute : Attribute 'Android.Runtime.RequiresPermissionAttribute' on 'Android.Accounts.AccountManager.RemoveAccount(Android.Accounts.Account, Android.App.Activity, Android.Accounts.IAccountManagerCallback, Android.OS.Handler)' changed from '[RequiresPermissionAttribute("android.permission.MANAGE_ACCOUNTS")]' in the contract to '[RequiresPermissionAttribute("android.permission.REMOVE_ACCOUNTS")]' in the implementation. CannotRemoveAttribute : Attribute 'Android.Runtime.RequiresPermissionAttribute' exists on 'Android.Bluetooth.BluetoothDevice.CreateInsecureL2capChannel(System.Int32)' in the contract but not the implementation. CannotRemoveAttribute : Attribute 'Android.Runtime.RequiresPermissionAttribute' exists on 'Android.Bluetooth.BluetoothDevice.CreateInsecureRfcommSocketToServiceRecord(Java.Util.UUID)' in the contract but not the implementation. @@ -48,3 +47,4 @@ MembersMustExist : Member 'public void Android.Telecom.CallControl.RequestVideoS TypesMustExist : Type 'Android.Text.IInputType' does not exist in the implementation but it does exist in the contract. TypesMustExist : Type 'Xamarin.Android.Net.AndroidClientHandler' does not exist in the implementation but it does exist in the contract. CannotRemoveBaseTypeOrInterface : Type 'Android.Views.InputMethods.EditorInfo' does not implement interface 'Android.Text.IInputType' in the implementation but it does in the contract. +CannotRemoveAttribute : Attribute 'System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute' exists on parameter 'targetType' on member 'Android.Graphics.ColorValueMarshaler.CreateGenericValue(Java.Interop.JniObjectReference, Java.Interop.JniObjectReferenceOptions, System.Type)' in the contract but not the implementation.