Skip to content

Commit e347cd3

Browse files
authored
[Mono.Android] Add ManagedValueManager, use with CoreCLR (#9973)
Context: #9962 Context: dotnet/java-interop@5852e6e `dotnet new maui -sc` fails under CoreCLR: D AndroidRuntime: Shutting down VM E AndroidRuntime: FATAL EXCEPTION: main E AndroidRuntime: Process: com.companyname.dotnetnewmauisamplecontent, PID: 6548 E AndroidRuntime: android.runtime.JavaProxyThrowable: [System.InvalidOperationException]: InvalidOperation_HandleIsNotInitialized E AndroidRuntime: at System.WeakReference`1.SetTarget + 0x18(Unknown Source) E AndroidRuntime: at Microsoft.Maui.ApplicationModel.ActivityLifecycleContextListener.set_Activity + 0x0(Unknown Source) E AndroidRuntime: at Microsoft.Maui.ApplicationModel.ActivityLifecycleContextListener.Android.App.Application.IActivityLifecycleCallbacks.OnActivityResumed + 0x0(Unknown Source) E AndroidRuntime: at Android.App.Application+IActivityLifecycleCallbacksInvoker.n_OnActivityResumed_Landroid_app_Activity_ + 0xe(Unknown Source) E AndroidRuntime: at crc64ba438d8f48cf7e75.ActivityLifecycleContextListener.n_onActivityResumed(Native Method) E AndroidRuntime: at crc64ba438d8f48cf7e75.ActivityLifecycleContextListener.onActivityResumed(ActivityLifecycleContextListener.java:42) E AndroidRuntime: at android.app.Application.dispatchActivityResumed(Application.java:431) E AndroidRuntime: at android.app.Activity.dispatchActivityResumed(Activity.java:1434) E AndroidRuntime: at android.app.Activity.onResume(Activity.java:1995) E AndroidRuntime: at androidx.fragment.app.FragmentActivity.onResume(FragmentActivity.java:309) E AndroidRuntime: at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1531) E AndroidRuntime: at android.app.Activity.performResume(Activity.java:8422) E AndroidRuntime: at android.app.ActivityThread.performResumeActivity(ActivityThread.java:4793) E AndroidRuntime: at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:4836) E AndroidRuntime: at android.app.servertransaction.ResumeActivityItem.execute(ResumeActivityItem.java:54) E AndroidRuntime: at android.app.servertransaction.ActivityTransactionItem.execute(ActivityTransactionItem.java:45) E AndroidRuntime: at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:176) E AndroidRuntime: at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97) E AndroidRuntime: at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2308) E AndroidRuntime: at android.os.Handler.dispatchMessage(Handler.java:106) E AndroidRuntime: at android.os.Looper.loopOnce(Looper.java:201) E AndroidRuntime: at android.os.Looper.loop(Looper.java:288) E AndroidRuntime: at android.app.ActivityThread.main(ActivityThread.java:7898) E AndroidRuntime: at java.lang.reflect.Method.invoke(Native Method) E AndroidRuntime: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548) E AndroidRuntime: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936) *A* likely explanation is that we're using the wrong `JniRuntime.JniValueManager` when running under CoreCLR: we're using `AndroidValueManager`, which uses `WeakReference<T>` to hold `IJavaPeerable` instances. As CoreCLR does not have a GC bridge, this means that any `IJavaPeerable` instance which is kept alive only by Java code -- such as `Activity` instances! -- will be collected, which at minimum will be "surprising". Fortunately, we already have a `JniRuntime.JniValueManager` which retains strong references to every created `IJavaPeerable` instance: `NativeAotValueManager`! The problem is that `NativeAotValueManager` is in `Microsoft.Android.Runtime.NativeAOT.dll`, which isn't usable from a CoreCLR context. Move `NativeAotValueManager` into `Mono.Android.dll`, renaming it `ManagedValueManager`. Update `JNIEnvInit.Initialize()` to use `ManagedValueManager` when running under CoreCLR. Relatedly: dotnet/java-interop@5852e6e3 obsoleted `JniRuntime.CreationOptions.ClassLoader_LoadClass_id`, so update `AndroidRuntime` and `AndroidRuntimeOptions` to no longer use that. * Suppress IL2072 CI was emitting a new warning: /Users/builder/azdo/_work/10/s/xamarin-android/src/Mono.Android/Microsoft.Android.Runtime/ManagedValueManager.cs(236,3): Trim analysis warning IL2072: Microsoft.Android.Runtime.ManagedValueManager.ActivateViaReflection(JniObjectReference, ConstructorInfo, Object[]): 'type' argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicConstructors', 'DynamicallyAccessedMemberTypes.NonPublicConstructors' in call to 'System.Runtime.CompilerServices.RuntimeHelpers.GetUninitializedObject(Type)'. The return value of method 'System.Reflection.MemberInfo.DeclaringType.get' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to. Suppress the warnings.
1 parent 8ce221c commit e347cd3

File tree

6 files changed

+36
-19
lines changed

6 files changed

+36
-19
lines changed

src/Microsoft.Android.Runtime.NativeAOT/Android.Runtime.NativeAOT/JavaInteropRuntime.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ static void init (IntPtr jnienv, IntPtr klass)
4141
var options = new NativeAotRuntimeOptions {
4242
EnvironmentPointer = jnienv,
4343
TypeManager = typeManager,
44-
ValueManager = new NativeAotValueManager (typeManager),
44+
ValueManager = new ManagedValueManager (),
4545
UseMarshalMemberBuilder = false,
4646
JniGlobalReferenceLogWriter = settings.GrefLog,
4747
JniLocalReferenceLogWriter = settings.LrefLog,

src/Microsoft.Android.Runtime.NativeAOT/Java.Interop/JreRuntime.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ static NativeAotRuntimeOptions CreateJreVM (NativeAotRuntimeOptions builder)
6161
builder.TypeManager ??= new NativeAotTypeManager ();
6262
#endif // NET
6363

64-
builder.ValueManager ??= new NativeAotValueManager (builder.TypeManager);
64+
builder.ValueManager ??= new ManagedValueManager ();
6565
builder.ObjectReferenceManager ??= new ManagedObjectReferenceManager (builder.JniGlobalReferenceLogWriter, builder.JniLocalReferenceLogWriter);
6666

6767
if (builder.InvocationPointer != IntPtr.Zero || builder.EnvironmentPointer != IntPtr.Zero)

src/Mono.Android/Android.Runtime/AndroidRuntime.cs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,14 @@ class AndroidRuntime : JniRuntime {
2323
internal AndroidRuntime (IntPtr jnienv,
2424
IntPtr vm,
2525
IntPtr classLoader,
26-
IntPtr classLoader_loadClass,
26+
JniRuntime.JniTypeManager? typeManager,
27+
JniRuntime.JniValueManager? valueManager,
2728
bool jniAddNativeMethodRegistrationAttributePresent)
2829
: base (new AndroidRuntimeOptions (jnienv,
2930
vm,
3031
classLoader,
31-
classLoader_loadClass,
32+
typeManager,
33+
valueManager,
3234
jniAddNativeMethodRegistrationAttributePresent))
3335
{
3436
// This is not ideal, but we need to set this while the runtime is initializing but we can't do it directly from the `JNIEnvInit.Initialize` method, since
@@ -93,16 +95,16 @@ class AndroidRuntimeOptions : JniRuntime.CreationOptions {
9395
public AndroidRuntimeOptions (IntPtr jnienv,
9496
IntPtr vm,
9597
IntPtr classLoader,
96-
IntPtr classLoader_loadClass,
98+
JniRuntime.JniTypeManager? typeManager,
99+
JniRuntime.JniValueManager? valueManager,
97100
bool jniAddNativeMethodRegistrationAttributePresent)
98101
{
99102
EnvironmentPointer = jnienv;
100103
ClassLoader = new JniObjectReference (classLoader, JniObjectReferenceType.Global);
101-
ClassLoader_LoadClass_id= classLoader_loadClass;
102104
InvocationPointer = vm;
103105
ObjectReferenceManager = new AndroidObjectReferenceManager ();
104-
TypeManager = new AndroidTypeManager (jniAddNativeMethodRegistrationAttributePresent);
105-
ValueManager = new AndroidValueManager ();
106+
TypeManager = typeManager ?? new AndroidTypeManager (jniAddNativeMethodRegistrationAttributePresent);
107+
ValueManager = valueManager ?? new AndroidValueManager ();
106108
UseMarshalMemberBuilder = false;
107109
JniAddNativeMethodRegistrationAttributePresent = jniAddNativeMethodRegistrationAttributePresent;
108110
}

src/Mono.Android/Android.Runtime/JNIEnvInit.cs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
using Java.Interop;
99
using Java.Interop.Tools.TypeNameMappings;
1010

11+
using Microsoft.Android.Runtime;
12+
1113
namespace Android.Runtime
1214
{
1315
static internal class JNIEnvInit
@@ -108,7 +110,14 @@ internal static unsafe void Initialize (JnienvInitializeArgs* args)
108110
java_class_loader = args->grefLoader;
109111

110112
BoundExceptionType = (BoundExceptionType)args->ioExceptionType;
111-
androidRuntime = new AndroidRuntime (args->env, args->javaVm, args->grefLoader, args->Loader_loadClass, args->jniAddNativeMethodRegistrationAttributePresent != 0);
113+
androidRuntime = new AndroidRuntime (
114+
args->env,
115+
args->javaVm,
116+
args->grefLoader,
117+
null,
118+
RuntimeType != DotNetRuntimeType.MonoVM ? new ManagedValueManager () : null,
119+
args->jniAddNativeMethodRegistrationAttributePresent != 0
120+
);
112121
ValueManager = androidRuntime.ValueManager;
113122

114123
IsRunningOnDesktop = args->isRunningOnDesktop == 1;
Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,15 @@
1616

1717
namespace Microsoft.Android.Runtime;
1818

19-
class NativeAotValueManager : JniRuntime.JniValueManager
19+
class ManagedValueManager : JniRuntime.JniValueManager
2020
{
2121
const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors;
2222

23-
readonly JniRuntime.JniTypeManager TypeManager;
2423
Dictionary<int, List<IJavaPeerable>>? RegisteredInstances = new Dictionary<int, List<IJavaPeerable>>();
2524

26-
public NativeAotValueManager(JniRuntime.JniTypeManager typeManager) =>
27-
TypeManager = typeManager;
25+
internal ManagedValueManager ()
26+
{
27+
}
2828

2929
public override void WaitForGCBridgeProcessing ()
3030
{
@@ -33,7 +33,7 @@ public override void WaitForGCBridgeProcessing ()
3333
public override void CollectPeers ()
3434
{
3535
if (RegisteredInstances == null)
36-
throw new ObjectDisposedException (nameof (NativeAotValueManager));
36+
throw new ObjectDisposedException (nameof (ManagedValueManager));
3737

3838
var peers = new List<IJavaPeerable> ();
3939

@@ -62,7 +62,7 @@ public override void CollectPeers ()
6262
public override void AddPeer (IJavaPeerable value)
6363
{
6464
if (RegisteredInstances == null)
65-
throw new ObjectDisposedException (nameof (NativeAotValueManager));
65+
throw new ObjectDisposedException (nameof (ManagedValueManager));
6666

6767
var r = value.PeerReference;
6868
if (!r.IsValid)
@@ -127,7 +127,7 @@ void WarnNotReplacing (int key, IJavaPeerable ignoreValue, IJavaPeerable keepVal
127127
public override IJavaPeerable? PeekPeer (JniObjectReference reference)
128128
{
129129
if (RegisteredInstances == null)
130-
throw new ObjectDisposedException (nameof (NativeAotValueManager));
130+
throw new ObjectDisposedException (nameof (ManagedValueManager));
131131

132132
if (!reference.IsValid)
133133
return null;
@@ -153,7 +153,7 @@ void WarnNotReplacing (int key, IJavaPeerable ignoreValue, IJavaPeerable keepVal
153153
public override void RemovePeer (IJavaPeerable value)
154154
{
155155
if (RegisteredInstances == null)
156-
throw new ObjectDisposedException (nameof (NativeAotValueManager));
156+
throw new ObjectDisposedException (nameof (ManagedValueManager));
157157

158158
if (value == null)
159159
throw new ArgumentNullException (nameof (value));
@@ -230,20 +230,25 @@ public override void ActivatePeer (IJavaPeerable? self, JniObjectReference refer
230230

231231
void ActivateViaReflection (JniObjectReference reference, ConstructorInfo cinfo, object?[]? argumentValues)
232232
{
233-
var declType = cinfo.DeclaringType ?? throw new NotSupportedException ("Do not know the type to create!");
233+
var declType = GetDeclaringType (cinfo);
234234

235235
#pragma warning disable IL2072
236236
var self = (IJavaPeerable) System.Runtime.CompilerServices.RuntimeHelpers.GetUninitializedObject (declType);
237237
#pragma warning restore IL2072
238238
self.SetPeerReference (reference);
239239

240240
cinfo.Invoke (self, argumentValues);
241+
242+
[UnconditionalSuppressMessage ("Trimming", "IL2073", Justification = "🤷‍♂️")]
243+
[return: DynamicallyAccessedMembers (Constructors)]
244+
Type GetDeclaringType (ConstructorInfo cinfo) =>
245+
cinfo.DeclaringType ?? throw new NotSupportedException ("Do not know the type to create!");
241246
}
242247

243248
public override List<JniSurfacedPeerInfo> GetSurfacedPeers ()
244249
{
245250
if (RegisteredInstances == null)
246-
throw new ObjectDisposedException (nameof (NativeAotValueManager));
251+
throw new ObjectDisposedException (nameof (ManagedValueManager));
247252

248253
lock (RegisteredInstances) {
249254
var peers = new List<JniSurfacedPeerInfo> (RegisteredInstances.Count);

src/Mono.Android/Mono.Android.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,7 @@
349349
<Compile Include="Java.Util.Concurrent.Atomic\AtomicInteger.cs" />
350350
<Compile Include="Java.Util.Concurrent.Atomic\AtomicLong.cs" />
351351
<Compile Include="Javax.Microedition.Khronos.Egl\EGLContext.cs" />
352+
<Compile Include="Microsoft.Android.Runtime\ManagedValueManager.cs" />
352353
<Compile Include="Org.Apache.Http.Impl.Conn\DefaultClientConnection.cs" />
353354
<Compile Include="Org.Apache.Http.Impl.Cookie\BasicClientCookie.cs" />
354355
<Compile Include="System.Drawing/PointConverter.cs" />

0 commit comments

Comments
 (0)