Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Mono.Android] add fallback for TypemapManagedToJava #9811

Closed
wants to merge 1 commit into from

Conversation

jonathanpeppers
Copy link
Member

The .NET MAUI template + NativeAOT currently crashes with:

02-18 15:59:24.575 12907 12907 E AndroidRuntime: net.dot.jni.internal.JavaProxyThrowable: System.InvalidProgramException: InvalidProgram_Specific, IntPtr Android.Runtime.JNIEnv.monodroid_typemap_managed_to_java(System.Type, Byte*)
02-18 15:59:24.575 12907 12907 E AndroidRuntime:    at Internal.Runtime.TypeLoaderExceptionHelper.CreateInvalidProgramException(ExceptionStringID, String) + 0x4c
02-18 15:59:24.575 12907 12907 E AndroidRuntime:    at Android.Runtime.JNIEnv.monodroid_typemap_managed_to_java(Type, Byte*) + 0x18
02-18 15:59:24.575 12907 12907 E AndroidRuntime:    at Android.Runtime.JNIEnv.TypemapManagedToJava(Type) + 0x104
02-18 15:59:24.575 12907 12907 E AndroidRuntime:    at Android.Runtime.JNIEnv.GetJniName(Type) + 0x1c
02-18 15:59:24.575 12907 12907 E AndroidRuntime:    at Android.Runtime.JNIEnv.FindClass(Type) + 0x38
02-18 15:59:24.575 12907 12907 E AndroidRuntime:    at Android.Runtime.JNIEnv.NewArray(IJavaObject[]) + 0x28
02-18 15:59:24.575 12907 12907 E AndroidRuntime:    at Android.Runtime.JNIEnv.NewArray[T](T[]) + 0x94
02-18 15:59:24.575 12907 12907 E AndroidRuntime:    at Android.Graphics.Drawables.LayerDrawable..ctor(Drawable[] layers) + 0xd4
02-18 15:59:24.575 12907 12907 E AndroidRuntime:    at Microsoft.Maui.Platform.MauiRippleDrawableExtensions.UpdateMauiRippleDrawableBackground(View, Paint, IButtonStroke, Func`1, Func`1, Action) + 0x2ac

This appears to be related to array usage, such as LayerDrawable.ctor(Drawable[]) in this example.

I can reproduce the same crash using a ColorStateList.ctor(int[][], int[]) in our NativeAOT "hello world" sample:

02-19 10:45:29.728 28692 28692 E AndroidRuntime: net.dot.jni.internal.JavaProxyThrowable: System.InvalidProgramException: InvalidProgram_Specific, IntPtr Android.Runtime.JNIEnv.monodroid_typemap_managed_to_java(System.Type, Byte*)
02-19 10:45:29.728 28692 28692 E AndroidRuntime:    at Internal.Runtime.TypeLoaderExceptionHelper.CreateInvalidProgramException(ExceptionStringID, String) + 0x4c
02-19 10:45:29.728 28692 28692 E AndroidRuntime:    at Android.Runtime.JNIEnv.monodroid_typemap_managed_to_java(Type, Byte*) + 0x18
02-19 10:45:29.728 28692 28692 E AndroidRuntime:    at Android.Runtime.JNIEnv.TypemapManagedToJava(Type) + 0x104
02-19 10:45:29.728 28692 28692 E AndroidRuntime:    at Android.Runtime.JNIEnv.GetJniName(Type) + 0x1c
02-19 10:45:29.728 28692 28692 E AndroidRuntime:    at Android.Runtime.JNIEnv.FindClass(Type) + 0x38
02-19 10:45:29.728 28692 28692 E AndroidRuntime:    at Android.Runtime.JNIEnv.NewArray[T](T[]) + 0xa8
02-19 10:45:29.728 28692 28692 E AndroidRuntime:    at Android.Content.Res.ColorStateList..ctor(Int32[][], Int32[]) + 0xdc
02-19 10:45:29.728 28692 28692 E AndroidRuntime:    at NativeAOT.MainActivity.OnCreate(Bundle savedInstanceState) + 0xb8

To fix this:

  • Introduce a new Android.Runtime.RuntimeFeature.UseReflectionForManagedToJava feature switch

  • If the switch is toggled, call JavaNativeTypeManager.ToJniName(System.Type) instead of TypemapManagedToJava().

  • Toggle the feature switch for NativeAOT.

In the future, we may enable this switch as a stopgap for getting other runtimes to launch successfully.

To reduce confusion, I also removed the managed side of JNIEnvInit.IsRunningOnDesktop, which was support for the Android designer. It was using the exact same fallback after attempting TypemapManagedToJava(). In a future PR, we could probably also remove the native C/C++ side of this flag.

const string FeatureSwitchPrefix = "Android.Runtime.RuntimeFeature.";

[FeatureSwitchDefinition ($"{FeatureSwitchPrefix}{nameof (UseReflectionForManagedToJava)}")]
internal static bool UseReflectionForManagedToJava { get; } =
Copy link
Member Author

@jonathanpeppers jonathanpeppers Feb 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Taking suggestions on a better name than UseReflectionForManagedToJava...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer that we not need the linker switch, and I think my earlier suggestion would prevent the need for this linker switch.

The .NET MAUI template + NativeAOT currently crashes with:

    02-18 15:59:24.575 12907 12907 E AndroidRuntime: net.dot.jni.internal.JavaProxyThrowable: System.InvalidProgramException: InvalidProgram_Specific, IntPtr Android.Runtime.JNIEnv.monodroid_typemap_managed_to_java(System.Type, Byte*)
    02-18 15:59:24.575 12907 12907 E AndroidRuntime:    at Internal.Runtime.TypeLoaderExceptionHelper.CreateInvalidProgramException(ExceptionStringID, String) + 0x4c
    02-18 15:59:24.575 12907 12907 E AndroidRuntime:    at Android.Runtime.JNIEnv.monodroid_typemap_managed_to_java(Type, Byte*) + 0x18
    02-18 15:59:24.575 12907 12907 E AndroidRuntime:    at Android.Runtime.JNIEnv.TypemapManagedToJava(Type) + 0x104
    02-18 15:59:24.575 12907 12907 E AndroidRuntime:    at Android.Runtime.JNIEnv.GetJniName(Type) + 0x1c
    02-18 15:59:24.575 12907 12907 E AndroidRuntime:    at Android.Runtime.JNIEnv.FindClass(Type) + 0x38
    02-18 15:59:24.575 12907 12907 E AndroidRuntime:    at Android.Runtime.JNIEnv.NewArray(IJavaObject[]) + 0x28
    02-18 15:59:24.575 12907 12907 E AndroidRuntime:    at Android.Runtime.JNIEnv.NewArray[T](T[]) + 0x94
    02-18 15:59:24.575 12907 12907 E AndroidRuntime:    at Android.Graphics.Drawables.LayerDrawable..ctor(Drawable[] layers) + 0xd4
    02-18 15:59:24.575 12907 12907 E AndroidRuntime:    at Microsoft.Maui.Platform.MauiRippleDrawableExtensions.UpdateMauiRippleDrawableBackground(View, Paint, IButtonStroke, Func`1, Func`1, Action) + 0x2ac

This appears to be related to array usage, such as
`LayerDrawable.ctor(Drawable[])` in this example.

I can reproduce the same crash using a `ColorStateList.ctor(int[][],
int[])` in our NativeAOT "hello world" sample:

    02-19 10:45:29.728 28692 28692 E AndroidRuntime: net.dot.jni.internal.JavaProxyThrowable: System.InvalidProgramException: InvalidProgram_Specific, IntPtr Android.Runtime.JNIEnv.monodroid_typemap_managed_to_java(System.Type, Byte*)
    02-19 10:45:29.728 28692 28692 E AndroidRuntime:    at Internal.Runtime.TypeLoaderExceptionHelper.CreateInvalidProgramException(ExceptionStringID, String) + 0x4c
    02-19 10:45:29.728 28692 28692 E AndroidRuntime:    at Android.Runtime.JNIEnv.monodroid_typemap_managed_to_java(Type, Byte*) + 0x18
    02-19 10:45:29.728 28692 28692 E AndroidRuntime:    at Android.Runtime.JNIEnv.TypemapManagedToJava(Type) + 0x104
    02-19 10:45:29.728 28692 28692 E AndroidRuntime:    at Android.Runtime.JNIEnv.GetJniName(Type) + 0x1c
    02-19 10:45:29.728 28692 28692 E AndroidRuntime:    at Android.Runtime.JNIEnv.FindClass(Type) + 0x38
    02-19 10:45:29.728 28692 28692 E AndroidRuntime:    at Android.Runtime.JNIEnv.NewArray[T](T[]) + 0xa8
    02-19 10:45:29.728 28692 28692 E AndroidRuntime:    at Android.Content.Res.ColorStateList..ctor(Int32[][], Int32[]) + 0xdc
    02-19 10:45:29.728 28692 28692 E AndroidRuntime:    at NativeAOT.MainActivity.OnCreate(Bundle savedInstanceState) + 0xb8

To fix this:

* Introduce a new `Android.Runtime.RuntimeFeature.UseReflectionForManagedToJava`
  feature switch

* If the switch is toggled, call
  `JavaNativeTypeManager.ToJniName(System.Type)` instead of
  `TypemapManagedToJava()`.

* Toggle the feature switch for NativeAOT.

In the future, we may enable this switch as a stopgap for getting
other runtimes to launch successfully.

To reduce confusion, I also removed the managed side of
`JNIEnvInit.IsRunningOnDesktop`, which was support for the Android
designer. It was using the exact same fallback after attempting
`TypemapManagedToJava()`. In a future PR, we could probably also
remove the native C/C++ side of this flag.
@jonathanpeppers jonathanpeppers force-pushed the dev/peppers/TypemapManagedToJava branch from 8c14733 to 203ce44 Compare February 19, 2025 17:09
Comment on lines +281 to +283
if (RuntimeFeature.UseReflectionForManagedToJava) {
return JavaNativeTypeManager.ToJniName (type);
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could actually move this check inside TypemapManagedToJava(), is that actually better?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you shouldn't be touching this file at all, because we now have a NativeAotTypeManager (7a772f0, 70bd636) and the problem is we're not using it.

As suggested on Discord, I think we instead want to update the codepath which is hitting GetJniName()/TypemapManagedToJava() in the first place, by updating JNIEnv.FindClass() to call:

var sig = JNIEnvInit.androidRuntime.TypeManager.GetTypeSignature(type);

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll try this to see if that is even simpler.

@@ -13,6 +13,7 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android.
<PropertyGroup>
<_AndroidRuntimePackRuntime>NativeAOT</_AndroidRuntimePackRuntime>
<AndroidCodegenTarget Condition=" '$(AndroidCodegenTarget)' == '' ">JavaInterop1</AndroidCodegenTarget>
<_AndroidUseReflectionForManagedToJava Condition=" '$(_AndroidUseReflectionForManagedToJava)' == '' ">true</_AndroidUseReflectionForManagedToJava>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this be set somewhere where it is not just NativeAOT specific and default to True when '$(_AndroidRuntime)' != 'MonoVM'?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can put the same thing here:

<!-- Default property values for CoreCLR -->
<PropertyGroup>
<_AndroidRuntimePackRuntime>CoreCLR</_AndroidRuntimePackRuntime>
</PropertyGroup>

If we for sure will need it at first, I can add it.

jonathanpeppers added a commit that referenced this pull request Feb 19, 2025
Context: #9811

The .NET MAUI template + NativeAOT currently crashes with:

    02-18 15:59:24.575 12907 12907 E AndroidRuntime: net.dot.jni.internal.JavaProxyThrowable: System.InvalidProgramException: InvalidProgram_Specific, IntPtr Android.Runtime.JNIEnv.monodroid_typemap_managed_to_java(System.Type, Byte*)
    02-18 15:59:24.575 12907 12907 E AndroidRuntime:    at Internal.Runtime.TypeLoaderExceptionHelper.CreateInvalidProgramException(ExceptionStringID, String) + 0x4c
    02-18 15:59:24.575 12907 12907 E AndroidRuntime:    at Android.Runtime.JNIEnv.monodroid_typemap_managed_to_java(Type, Byte*) + 0x18
    02-18 15:59:24.575 12907 12907 E AndroidRuntime:    at Android.Runtime.JNIEnv.TypemapManagedToJava(Type) + 0x104
    02-18 15:59:24.575 12907 12907 E AndroidRuntime:    at Android.Runtime.JNIEnv.GetJniName(Type) + 0x1c
    02-18 15:59:24.575 12907 12907 E AndroidRuntime:    at Android.Runtime.JNIEnv.FindClass(Type) + 0x38
    02-18 15:59:24.575 12907 12907 E AndroidRuntime:    at Android.Runtime.JNIEnv.NewArray(IJavaObject[]) + 0x28
    02-18 15:59:24.575 12907 12907 E AndroidRuntime:    at Android.Runtime.JNIEnv.NewArray[T](T[]) + 0x94
    02-18 15:59:24.575 12907 12907 E AndroidRuntime:    at Android.Graphics.Drawables.LayerDrawable..ctor(Drawable[] layers) + 0xd4
    02-18 15:59:24.575 12907 12907 E AndroidRuntime:    at Microsoft.Maui.Platform.MauiRippleDrawableExtensions.UpdateMauiRippleDrawableBackground(View, Paint, IButtonStroke, Func`1, Func`1, Action) + 0x2ac

This appears to be related to array usage, such as `LayerDrawable.ctor(Drawable[])` in this example.

I can reproduce the same crash using a `ColorStateList.ctor(int[][], int[])` in our NativeAOT "hello world" sample:

    02-19 10:45:29.728 28692 28692 E AndroidRuntime: net.dot.jni.internal.JavaProxyThrowable: System.InvalidProgramException: InvalidProgram_Specific, IntPtr Android.Runtime.JNIEnv.monodroid_typemap_managed_to_java(System.Type, Byte*)
    02-19 10:45:29.728 28692 28692 E AndroidRuntime:    at Internal.Runtime.TypeLoaderExceptionHelper.CreateInvalidProgramException(ExceptionStringID, String) + 0x4c
    02-19 10:45:29.728 28692 28692 E AndroidRuntime:    at Android.Runtime.JNIEnv.monodroid_typemap_managed_to_java(Type, Byte*) + 0x18
    02-19 10:45:29.728 28692 28692 E AndroidRuntime:    at Android.Runtime.JNIEnv.TypemapManagedToJava(Type) + 0x104
    02-19 10:45:29.728 28692 28692 E AndroidRuntime:    at Android.Runtime.JNIEnv.GetJniName(Type) + 0x1c
    02-19 10:45:29.728 28692 28692 E AndroidRuntime:    at Android.Runtime.JNIEnv.FindClass(Type) + 0x38
    02-19 10:45:29.728 28692 28692 E AndroidRuntime:    at Android.Runtime.JNIEnv.NewArray[T](T[]) + 0xa8
    02-19 10:45:29.728 28692 28692 E AndroidRuntime:    at Android.Content.Res.ColorStateList..ctor(Int32[][], Int32[]) + 0xdc
    02-19 10:45:29.728 28692 28692 E AndroidRuntime:    at NativeAOT.MainActivity.OnCreate(Bundle savedInstanceState) + 0xb8

As an alternative to #9811, we can update `JNIEnv.FindClass(Type)` to
go through `TypeManager` instead of using `TypemapManagedToJava`.

After this change, the sample works and prints a log message
indicating `ColorStateList` is created successfully:

    02-19 13:12:25.924  2665  2665 D NativeAOT: MainActivity.OnCreate() ColorStateList: ColorStateList{mThemeAttrs=nullmChangingConfigurations=0mStateSpecs=[[0, 1]]mColors=[0, 1]mDefaultColor=0}
jonpryor pushed a commit that referenced this pull request Feb 21, 2025
Changes: dotnet/java-interop@f30e420...9dea87d

  * dotnet/java-interop@9dea87dc: [Java.Interop] .GetTypeSignature() supports unsigned types (dotnet/java-interop#1312)
  * dotnet/java-interop@1cfb4f4d: [generator] Add support for emitting `[UnsupportedOSPlatform]` (dotnet/java-interop#1307)

Context: #9811

The .NET MAUI template + NativeAOT currently crashes with:

	E AndroidRuntime: net.dot.jni.internal.JavaProxyThrowable: System.InvalidProgramException: InvalidProgram_Specific, IntPtr Android.Runtime.JNIEnv.monodroid_typemap_managed_to_java(System.Type, Byte*)
	E AndroidRuntime:    at Internal.Runtime.TypeLoaderExceptionHelper.CreateInvalidProgramException(ExceptionStringID, String) + 0x4c
	E AndroidRuntime:    at Android.Runtime.JNIEnv.monodroid_typemap_managed_to_java(Type, Byte*) + 0x18
	E AndroidRuntime:    at Android.Runtime.JNIEnv.TypemapManagedToJava(Type) + 0x104
	E AndroidRuntime:    at Android.Runtime.JNIEnv.GetJniName(Type) + 0x1c
	E AndroidRuntime:    at Android.Runtime.JNIEnv.FindClass(Type) + 0x38
	E AndroidRuntime:    at Android.Runtime.JNIEnv.NewArray(IJavaObject[]) + 0x28
	E AndroidRuntime:    at Android.Runtime.JNIEnv.NewArray[T](T[]) + 0x94
	E AndroidRuntime:    at Android.Graphics.Drawables.LayerDrawable..ctor(Drawable[] layers) + 0xd4
	E AndroidRuntime:    at Microsoft.Maui.Platform.MauiRippleDrawableExtensions.UpdateMauiRippleDrawableBackground(View, Paint, IButtonStroke, Func`1, Func`1, Action) + 0x2ac

This appears to be related to array usage, such as
`LayerDrawable.ctor(Drawable[])` in this example.

I can reproduce the same crash using a
`ColorStateList.ctor(int[][], int[])` in `samples/NativeAOT`:

	E AndroidRuntime: net.dot.jni.internal.JavaProxyThrowable: System.InvalidProgramException: InvalidProgram_Specific, IntPtr Android.Runtime.JNIEnv.monodroid_typemap_managed_to_java(System.Type, Byte*)
	E AndroidRuntime:    at Internal.Runtime.TypeLoaderExceptionHelper.CreateInvalidProgramException(ExceptionStringID, String) + 0x4c
	E AndroidRuntime:    at Android.Runtime.JNIEnv.monodroid_typemap_managed_to_java(Type, Byte*) + 0x18
	E AndroidRuntime:    at Android.Runtime.JNIEnv.TypemapManagedToJava(Type) + 0x104
	E AndroidRuntime:    at Android.Runtime.JNIEnv.GetJniName(Type) + 0x1c
	E AndroidRuntime:    at Android.Runtime.JNIEnv.FindClass(Type) + 0x38
	E AndroidRuntime:    at Android.Runtime.JNIEnv.NewArray[T](T[]) + 0xa8
	E AndroidRuntime:    at Android.Content.Res.ColorStateList..ctor(Int32[][], Int32[]) + 0xdc
	E AndroidRuntime:    at NativeAOT.MainActivity.OnCreate(Bundle savedInstanceState) + 0xb8

Update `JNIEnv.FindClass(Type)` to go through `TypeManager` instead
of using `TypemapManagedToJava`.  This avoids the P/Invoke which is
causing the crash (f800c1a).

Note that we can't directly use
`JniRuntime.JniTypeManager.GetTypeSignature()`, as the previous use
of `JavaNativeTypeManager.ToJniName(Type)` would default to using
`java/lang/Object` if there was no typemap entry for `type`.

After this change, the sample works and prints a log message
indicating `ColorStateList` is created successfully:

	D NativeAOT: MainActivity.OnCreate() ColorStateList: ColorStateList{mThemeAttrs=nullmChangingConfigurations=0mStateSpecs=[[0, 1]]mColors=[0, 1]mDefaultColor=0}
@jonathanpeppers
Copy link
Member Author

I will keep this around, if we ever need to bring it back.

We may not need it due to:

But it is a good example of setting up a trimmer feature switch.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants