From dbb0b92035d4ebb40dd3fb2768e20ac7cbef43c4 Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Mon, 3 Feb 2025 13:09:31 -0600 Subject: [PATCH] [NativeAOT] Add support for `Application` subclasses (#9716) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Context: https://github.com/xamarin/monodroid/commit/1a931e276ad38c28b944b83157573a55044c1be3 `android.app.Application` and `android.app.Instrumentation` are ["special"][0], in terms of app startup and type registration, because MonoVM is initialized via a `ContentProvider`, which is constructed *after* `Application` instance is constructed. In broad terms: 1. `Application` instance is created. (Specific type is via [`//application/@android:name`][1].) 2. `ContentProvider`s are created, including `MonoRuntimeProvider` (MonoVM) or `NativeAotRuntimeProvider` (NativeAOT). 3. `*RuntimeProvider.attachInfo()` invoked, provided (1). 4. `*RuntimeProvider.attachInfo()` does whatever runtime init is required, e.g. MonoVM's `MonoRuntimeProvider` calls `MonoPackageManager.LoadApplication()`. We cannot dispatch Java `native` methods into managed code until *after* (4) finishes. Meanwhile, we allow C# to subclass `Android.App.Application` and override methods like `Application.OnCreate()`: [Application (Name = "my.MainApplication")] public partial class MainApplication : Application { public override void OnCreate () { base.OnCreate (); } } How does that work? It works via a leaky abstraction: unlike other types, the Java Callable Wrapper (JCW) for `Application` and `Instrumentation` subclasses lacks: 1. A `Runtime.register()` invocation in the static constructor, and 2. A call to `TypeManager.Activate()` in the instance constructor. Compare an `Activity` JCW: public /* partial */ class MainActivity extends android.app.Activity { static { __md_methods = "…"; mono.android.Runtime.register ("….MainActivity, …", MainActivity.class, __md_methods); } public MainActivity () { super (); if (getClass () == MainActivity.class) { mono.android.TypeManager.Activate ("….MainActivity, …", "", this, new java.lang.Object[] { }); } } } to an `Application` JCW: public /* partial */ class MainApplication extends android.app.Application { { static { // mostly empty }} public MainApplication () { // Used to provide `Android.App.Application.Context` property mono.MonoPackageManager.setContext (this); // No `TypeManager.Activate(…)` call } } Instead of a `Runtime.register()` invocation in the e.g. `MainApplication` static constructor, `MainApplication` is instead registered via `ApplicationRegistration.registerApplications()`, which is: 1. Generated via the `` task, and 2. Invoked from `MonoPackageManager.LoadApplication()`. "Later", when the Java `Application.onCreate()` method is invoked, the `Application.n_OnCreate()` marshal method will call `Java.Lang.Object.GetObject(…)`. This will look for the `(IntPtr, JniHandleOwnership)` "activation constructor", which must be present on the C# type: public partial class MainApplication : Application { public MainApplication (IntPtr handle, JniHandleOwnership transfer) : base (handle, transfer) { } } To support `Application` subclasses under NativeAOT, we need to ensure that the `` task creates `ApplicationRegistration.java`, and update `NativeAotRuntimeProvider` to invoke `ApplicationRegistration.registerApplications()`. We also need various changes to `NativeAotValueManager` so that we can create the `MainApplication` "proxy" instance via the activation constructor. Other changes: * Change the package name for `ApplicationRegistration` from `mono.android` to `net.dot.android`. * Stop using `AndroidValueManager` and instead copy [`ManagedValueManager`][2] into `NativeAotValueManager`. * Allow `CodeGenerationTarget` to be explicitly provided to ``, instead of inferring it based on `$(_AndroidRuntime)`. [0]: https://learn.microsoft.com/en-us/previous-versions/xamarin/android/internals/architecture#java-activation [1]: https://developer.android.com/guide/topics/manifest/application-element#nm [2]: https://github.com/dotnet/java-interop/blob/dd3c1d0514addfe379f050627b3e97493e985da6/src/Java.Runtime.Environment/Java.Interop/ManagedValueManager.cs --- samples/NativeAOT/JavaInteropRuntime.cs | 5 +- samples/NativeAOT/MainActivity.cs | 7 +- samples/NativeAOT/MainApplication.cs | 23 + .../NativeAOT/NativeAotRuntimeProvider.java | 2 + samples/NativeAOT/NativeAotTypeManager.cs | 2 + samples/NativeAOT/NativeAotValueManager.cs | 411 +++++++++++++++++- .../Microsoft.Android.Sdk.NativeAOT.targets | 1 + .../Resources/ApplicationRegistration.java | 2 +- .../Tasks/GenerateJavaStubs.cs | 35 +- .../Xamarin.Android.Build.Tests/BuildTest.cs | 4 +- .../Utilities/MonoAndroidHelper.cs | 10 + .../Xamarin.Android.Common.targets | 1 + src/java-runtime/java-runtime.targets | 2 +- .../java/mono/android/MonoPackageManager.java | 2 +- .../dot/android}/ApplicationRegistration.java | 2 +- 15 files changed, 481 insertions(+), 28 deletions(-) create mode 100644 samples/NativeAOT/MainApplication.cs rename src/java-runtime/java/{mono/android/app => net/dot/android}/ApplicationRegistration.java (85%) diff --git a/samples/NativeAOT/JavaInteropRuntime.cs b/samples/NativeAOT/JavaInteropRuntime.cs index 4e54afeb3ba..8c86e5d264b 100644 --- a/samples/NativeAOT/JavaInteropRuntime.cs +++ b/samples/NativeAOT/JavaInteropRuntime.cs @@ -34,10 +34,11 @@ static void JNI_OnUnload (IntPtr vm, IntPtr reserved) static void init (IntPtr jnienv, IntPtr klass) { try { + var typeManager = new NativeAotTypeManager (); var options = new JreRuntimeOptions { EnvironmentPointer = jnienv, - TypeManager = new NativeAotTypeManager (), - ValueManager = new NativeAotValueManager (), + TypeManager = typeManager, + ValueManager = new NativeAotValueManager (typeManager), UseMarshalMemberBuilder = false, JniGlobalReferenceLogWriter = new LogcatTextWriter (AndroidLogLevel.Debug, "NativeAot:GREF"), JniLocalReferenceLogWriter = new LogcatTextWriter (AndroidLogLevel.Debug, "NativeAot:LREF"), diff --git a/samples/NativeAOT/MainActivity.cs b/samples/NativeAOT/MainActivity.cs index 545983d62fe..aaedc0aef2d 100644 --- a/samples/NativeAOT/MainActivity.cs +++ b/samples/NativeAOT/MainActivity.cs @@ -1,15 +1,18 @@ using Android.Runtime; +using Android.Util; using System.Reflection; using System.Runtime.InteropServices; namespace NativeAOT; -[Register("my/MainActivity")] // Required for typemap in NativeAotTypeManager -[Activity(Label = "@string/app_name", MainLauncher = true)] +// Name required for typemap in NativeAotTypeManager +[Activity (Label = "@string/app_name", MainLauncher = true, Name = "my.MainActivity")] public class MainActivity : Activity { protected override void OnCreate(Bundle? savedInstanceState) { + Log.Debug ("NativeAOT", "MainActivity.OnCreate()"); + base.OnCreate(savedInstanceState); // Set our view from the "main" layout resource diff --git a/samples/NativeAOT/MainApplication.cs b/samples/NativeAOT/MainApplication.cs new file mode 100644 index 00000000000..e3f2c6f546f --- /dev/null +++ b/samples/NativeAOT/MainApplication.cs @@ -0,0 +1,23 @@ +using Android.Runtime; +using Android.Util; + +/// +/// NOTE: This class is not required, but used for testing Android.App.Application subclasses. +/// Name required for typemap in NativeAotTypeManager +/// +[Application (Name = "my.MainApplication")] +public class MainApplication : Application +{ + public MainApplication (IntPtr handle, JniHandleOwnership transfer) + : base (handle, transfer) + { + Log.Debug ("NativeAOT", $"Application..ctor({handle.ToString ("x2")}, {transfer})"); + } + + public override void OnCreate () + { + Log.Debug ("NativeAOT", "Application.OnCreate()"); + + base.OnCreate (); + } +} diff --git a/samples/NativeAOT/NativeAotRuntimeProvider.java b/samples/NativeAOT/NativeAotRuntimeProvider.java index b87f23f25c7..6de591501d5 100644 --- a/samples/NativeAOT/NativeAotRuntimeProvider.java +++ b/samples/NativeAOT/NativeAotRuntimeProvider.java @@ -21,6 +21,8 @@ public boolean onCreate() { public void attachInfo(android.content.Context context, android.content.pm.ProviderInfo info) { Log.d(TAG, "NativeAotRuntimeProvider.attachInfo(): calling JavaInteropRuntime.init()…"); JavaInteropRuntime.init(); + // NOTE: only required for custom applications + net.dot.android.ApplicationRegistration.registerApplications(); super.attachInfo (context, info); } diff --git a/samples/NativeAOT/NativeAotTypeManager.cs b/samples/NativeAOT/NativeAotTypeManager.cs index 29499d17847..e8d06af9911 100644 --- a/samples/NativeAOT/NativeAotTypeManager.cs +++ b/samples/NativeAOT/NativeAotTypeManager.cs @@ -13,12 +13,14 @@ partial class NativeAotTypeManager : JniRuntime.JniTypeManager { // TODO: list of types specific to this application Dictionary typeMappings = new () { ["android/app/Activity"] = typeof (Android.App.Activity), + ["android/app/Application"] = typeof (Android.App.Application), ["android/content/Context"] = typeof (Android.Content.Context), ["android/content/ContextWrapper"] = typeof (Android.Content.ContextWrapper), ["android/os/BaseBundle"] = typeof (Android.OS.BaseBundle), ["android/os/Bundle"] = typeof (Android.OS.Bundle), ["android/view/ContextThemeWrapper"] = typeof (Android.Views.ContextThemeWrapper), ["my/MainActivity"] = typeof (MainActivity), + ["my/MainApplication"] = typeof (MainApplication), }; public NativeAotTypeManager () diff --git a/samples/NativeAOT/NativeAotValueManager.cs b/samples/NativeAOT/NativeAotValueManager.cs index 8fb2c55d4e2..e233af7a480 100644 --- a/samples/NativeAOT/NativeAotValueManager.cs +++ b/samples/NativeAOT/NativeAotValueManager.cs @@ -1,10 +1,415 @@ +// 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.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 NativeAOT; -// TODO: make a NativeAOT-specific implementation of AndroidValueManager -// This is enough for "Hello World" to launch -internal class NativeAotValueManager : AndroidValueManager +internal class NativeAotValueManager : JniRuntime.JniValueManager { + const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors; + readonly JniRuntime.JniTypeManager TypeManager; + Dictionary>? RegisteredInstances = new Dictionary>(); + + public NativeAotValueManager(JniRuntime.JniTypeManager typeManager) => + TypeManager = typeManager; + + public override void WaitForGCBridgeProcessing () + { + } + + public override void CollectPeers () + { + if (RegisteredInstances == null) + throw new ObjectDisposedException (nameof (NativeAotValueManager)); + + 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 (NativeAotValueManager)); + + var r = value.PeerReference; + if (!r.IsValid) + throw new ObjectDisposedException (value.GetType ().FullName); + var o = PeekPeer (value.PeerReference); + if (o != null) + return; + + 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"), + RuntimeHelpers.GetHashCode (ignoreValue).ToString ("x"), + ignoreValue.GetType ().FullName, + JniEnvironment.Types.GetJniTypeNameFromInstance (ignoreValue.PeerReference), + keepValue.PeerReference.ToString (), + RuntimeHelpers.GetHashCode (keepValue).ToString ("x"), + keepValue.GetType ().FullName, + JniEnvironment.Types.GetJniTypeNameFromInstance (keepValue.PeerReference)); + } + + public override IJavaPeerable? PeekPeer (JniObjectReference reference) + { + if (RegisteredInstances == null) + throw new ObjectDisposedException (nameof (NativeAotValueManager)); + + 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 (NativeAotValueManager)); + + 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"), + RuntimeHelpers.GetHashCode (value).ToString ("x"), + 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"), + RuntimeHelpers.GetHashCode (value).ToString ("x"), + value.GetType ().ToString ()); + } + value.SetPeerReference (new JniObjectReference ()); + JniObjectReference.Dispose (ref h); + value.Finalized (); + } + + public override void ActivatePeer (IJavaPeerable? self, JniObjectReference reference, ConstructorInfo cinfo, object?[]? argumentValues) + { + try { + ActivateViaReflection (reference, cinfo, argumentValues); + } catch (Exception e) { + var m = string.Format ("Could not activate {{ PeerReference={0} IdentityHashCode=0x{1} Java.Type={2} }} for managed type '{3}'.", + reference, + GetJniIdentityHashCode (reference).ToString ("x"), + JniEnvironment.Types.GetJniTypeNameFromInstance (reference), + cinfo.DeclaringType?.FullName); + Debug.WriteLine (m); + + throw new NotSupportedException (m, e); + } + } + + void ActivateViaReflection (JniObjectReference reference, ConstructorInfo cinfo, object?[]? argumentValues) + { + var declType = cinfo.DeclaringType ?? throw new NotSupportedException ("Do not know the type to create!"); + +#pragma warning disable IL2072 + var self = (IJavaPeerable) System.Runtime.CompilerServices.RuntimeHelpers.GetUninitializedObject (declType); +#pragma warning restore IL2072 + self.SetPeerReference (reference); + + cinfo.Invoke (self, argumentValues); + } + + public override List GetSurfacedPeers () + { + if (RegisteredInstances == null) + throw new ObjectDisposedException (nameof (NativeAotValueManager)); + + 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; + } + } + + public override IJavaPeerable? CreatePeer ( + ref JniObjectReference reference, + JniObjectReferenceOptions transfer, + [DynamicallyAccessedMembers (Constructors)] + Type? targetType) + { + 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 proxy = CreatePeerProxy (ref refClass, targetType, ref reference, transfer); + + if (proxy == null) { + throw new NotSupportedException (string.Format ("Could not find an appropriate constructable wrapper type for Java type '{0}', targetType='{1}'.", + JniEnvironment.Types.GetJniTypeNameFromInstance (reference), targetType)); + } + + proxy.SetJniManagedPeerState (proxy.JniManagedPeerState | JniManagedPeerStates.Replaceable); + return proxy; + } + + [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 (global::Java.Interop.JavaException); + return type; + } + + IJavaPeerable? CreatePeerProxy ( + ref JniObjectReference klass, + [DynamicallyAccessedMembers (Constructors)] + Type fallbackType, + ref JniObjectReference reference, + JniObjectReferenceOptions options) + { + var jniTypeName = JniEnvironment.Types.GetJniTypeNameFromClass (klass); + + Type? type = null; + while (jniTypeName != null) { + JniTypeSignature sig; + if (!JniTypeSignature.TryParse (jniTypeName, out sig)) + return null; + + type = Runtime.TypeManager.GetType (sig); + + if (type != null) { + var peer = TryCreatePeerProxy (type, ref reference, options); + if (peer != null) { + 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 TryCreatePeerProxy (fallbackType, ref reference, options); + } + + [return: DynamicallyAccessedMembers (Constructors)] + static Type? GetInvokerType (Type type) + { + // https://github.com/xamarin/xamarin-android/blob/5472eec991cc075e4b0c09cd98a2331fb93aa0f3/src/Microsoft.Android.Sdk.ILLink/MarkJavaObjects.cs#L176-L186 + const string makeGenericTypeMessage = "Generic 'Invoker' types are preserved by the MarkJavaObjects trimmer step."; + + [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 + + var signature = type.GetCustomAttribute (); + if (signature == null || signature.InvokerType == null) { + return null; + } + + Type[] arguments = type.GetGenericArguments (); + if (arguments.Length == 0) + return signature.InvokerType; + + return MakeGenericType (signature.InvokerType, arguments); + } + + const BindingFlags ActivationConstructorBindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; + + + static readonly Type[] XAConstructorSignature = new Type [] { typeof (IntPtr), typeof (JniHandleOwnership) }; + static readonly Type[] JIConstructorSignature = new Type [] { typeof (JniObjectReference).MakeByRefType (), typeof (JniObjectReferenceOptions) }; + + protected virtual IJavaPeerable? TryCreatePeerProxy (Type type, ref JniObjectReference reference, JniObjectReferenceOptions options) + { + var c = type.GetConstructor (ActivationConstructorBindingFlags, null, XAConstructorSignature, null); + if (c != null) { + var args = new object[] { + reference.Handle, + JniHandleOwnership.DoNotTransfer, + }; + var p = (IJavaPeerable) c.Invoke (args); + JniObjectReference.Dispose (ref reference, options); + return p; + } + c = type.GetConstructor (ActivationConstructorBindingFlags, null, JIConstructorSignature, null); + if (c != null) { + var args = new object[] { + reference, + options, + }; + var p = (IJavaPeerable) c.Invoke (args); + reference = (JniObjectReference) args [0]; + return p; + } + return null; + } } diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets index 685e42403f5..64dd729f9ed 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets @@ -12,6 +12,7 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android. <_AndroidRuntimePackRuntime>NativeAOT + <_AndroidCodeGenerationTarget Condition=" '$(_AndroidCodeGenerationTarget)' == '' ">JavaInterop1 true diff --git a/src/Xamarin.Android.Build.Tasks/Resources/ApplicationRegistration.java b/src/Xamarin.Android.Build.Tasks/Resources/ApplicationRegistration.java index 5f86173d6cd..f14f681c094 100644 --- a/src/Xamarin.Android.Build.Tasks/Resources/ApplicationRegistration.java +++ b/src/Xamarin.Android.Build.Tasks/Resources/ApplicationRegistration.java @@ -1,4 +1,4 @@ -package mono.android.app; +package net.dot.android; public class ApplicationRegistration { diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/GenerateJavaStubs.cs b/src/Xamarin.Android.Build.Tasks/Tasks/GenerateJavaStubs.cs index 766ec7c1465..2fa90dca06e 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/GenerateJavaStubs.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/GenerateJavaStubs.cs @@ -96,7 +96,10 @@ public class GenerateJavaStubs : AndroidTask [Required] public string AndroidRuntime { get; set; } = ""; + public string CodeGenerationTarget { get; set; } = ""; + AndroidRuntime androidRuntime; + JavaPeerStyle codeGenerationTarget; internal const string AndroidSkipJavaStubGeneration = "AndroidSkipJavaStubGeneration"; @@ -104,6 +107,7 @@ public override bool RunTask () { try { androidRuntime = MonoAndroidHelper.ParseAndroidRuntime (AndroidRuntime); + codeGenerationTarget = MonoAndroidHelper.ParseCodeGenerationTarget (CodeGenerationTarget); bool useMarshalMethods = !Debug && EnableMarshalMethods; Run (useMarshalMethods); } catch (XamarinAndroidException e) { @@ -302,22 +306,21 @@ void GenerateAdditionalProviderSources (NativeCodeGenState codeGenState, IList MergeManifest (NativeCodeGenState codeGenState, Dictionary allJavaTypes, List javaTypesForJCW) = ScanForJavaTypes (resolver, tdCache, assemblies, userAssemblies, useMarshalMethods); var jcwContext = new JCWGeneratorContext (arch, resolver, assemblies.Values, javaTypesForJCW, tdCache, useMarshalMethods); var jcwGenerator = new JCWGenerator (Log, jcwContext) { - CodeGenerationTarget = androidRuntime == Xamarin.Android.Tasks.AndroidRuntime.MonoVM ? JavaPeerStyle.XAJavaInterop1 : JavaPeerStyle.JavaInterop1 + CodeGenerationTarget = codeGenerationTarget, }; bool success; diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest.cs index 48c1ed3685b..441bd42e5ac 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest.cs @@ -1093,12 +1093,12 @@ public void RemoveOldMonoPackageManager () }; var intermediate = Path.Combine (Root, b.ProjectDirectory, proj.IntermediateOutputPath); var oldMonoPackageManager = Path.Combine (intermediate, "android", "src", "mono", "MonoPackageManager.java"); - var notifyTimeZoneChanges = Path.Combine (intermediate, "android", "src", "mono", "android", "app", "NotifyTimeZoneChanges.java"); + var notifyTimeZoneChanges = Path.Combine (intermediate, "android", "src", "net", "dot", "android", "NotifyTimeZoneChanges.java"); Directory.CreateDirectory (Path.GetDirectoryName (notifyTimeZoneChanges)); File.WriteAllText (oldMonoPackageManager, @"package mono; public class MonoPackageManager { } class MonoPackageManager_Resources { }"); - File.WriteAllText (notifyTimeZoneChanges, @"package mono.android.app; + File.WriteAllText (notifyTimeZoneChanges, @"package net.dot.android; public class ApplicationRegistration { }"); var oldMonoPackageManagerClass = Path.Combine (intermediate, "android", "bin", "classes" , "mono", "MonoPackageManager.class"); File.WriteAllText (oldMonoPackageManagerClass, ""); diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.cs b/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.cs index 1367ded5abf..5659fba2d37 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.cs @@ -9,6 +9,7 @@ using System.Threading; using Xamarin.Android.Tools; using Xamarin.Tools.Zip; +using Java.Interop.Tools.JavaCallableWrappers; #if MSBUILD using Microsoft.Android.Build.Tasks; @@ -830,5 +831,14 @@ public static AndroidRuntime ParseAndroidRuntime (string androidRuntime) // Default runtime is MonoVM return AndroidRuntime.MonoVM; } + + public static JavaPeerStyle ParseCodeGenerationTarget (string codeGenerationTarget) + { + if (Enum.TryParse (codeGenerationTarget, ignoreCase: true, out JavaPeerStyle style)) + return style; + + // Default is XAJavaInterop1 + return JavaPeerStyle.XAJavaInterop1; + } } } diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets index 2739e628b27..3cf94e2657e 100644 --- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets +++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets @@ -1501,6 +1501,7 @@ because xbuild doesn't support framework reference assemblies. - +