Skip to content

[WIP] turn NativeAOT sample into a .NET MAUI app #9747

New issue

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

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

Already on GitHub? # to your account

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

jonathanpeppers
Copy link
Member

@jonathanpeppers jonathanpeppers commented Feb 3, 2025

Unsure yet if this should ever be merged, as it requires a lot of manual steps to even build it. I'm going to leave instructions here for my future self or others.

These are the list of steps to test a .NET MAUI app on NativeAOT:

Steps already done in this PR

Steps to build the sample from here

D:\src\dotnet\maui\.dotnet\dotnet build -c Release -p:PublishAot=true

It launches!

image

@jonathanpeppers jonathanpeppers added the do-not-merge PR should not be merged. label Feb 3, 2025
jonpryor added a commit to dotnet/java-interop that referenced this pull request Feb 4, 2025
Context: dotnet/android#9747
Context: https://discord.com/channels/732297728826277939/732297837953679412/1336353039031734352
Context: https://discord.com/channels/732297728826277939/732297837953679412/1336358257769316372

The `[Register]` attribute provides "connector method" names, and for
interface methods this will also include the name of the type which
declares the method, which itself may be in a nested type:

	namespace Android.App {
	  public partial class Application {
	    public partial interface IActivityLifecycleCallbacks : IJavaObject, IJavaPeerable {
	      [Register (
	          name: "onActivityCreated",
	          signature: "(Landroid/app/Activity;Landroid/os/Bundle;)V",
	          connector: "GetOnActivityCreated_Landroid_app_Activity_Landroid_os_Bundle_Handler:Android.App.Application/IActivityLifecycleCallbacksInvoker, Mono.Android, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null")]
	      void OnActivityCreated (Android.App.Activity activity, Android.OS.Bundle? savedInstanceState);

	      // …
	    }
	  }
	}

This output has been largely unchanged for *years*, but there is a
problem with it: the `connector` parameter contains a nested type,
and uses `/` to separate the "outer" type from the "inner" type.

This works on MonoVM.

This *fails* on NativeAOT and CoreCLR:

	Could not resolve type 'Android.App.Application/IActivityLifecycleCallbacksInvoker' in assembly 'Mono.Android, Version=0.0.0.0, Culture=neutral, PublicKeyToken=84e04ff9cfb79065'.

The `/` needs to be a `+` in order for `Type.GetType()` to find it:

	[Register (
	    name: "onActivityCreated",
	    signature: "(Landroid/app/Activity;Landroid/os/Bundle;)V",
	    connector: "GetOnActivityCreated_Landroid_app_Activity_Landroid_os_Bundle_Handler:Android.App.Application+IActivityLifecycleCallbacksInvoker, Mono.Android, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null")]
	void OnActivityCreated (Android.App.Activity activity, Android.OS.Bundle? savedInstanceState);

Update `generator` so that `+` is used within the `connector`
parameter.

However, to maintain compatibility with existing binding assemblies,
update `Java.Interop.Tools.JavaCallableWrappers` so that it replaces
`/` with `+` on import, so that the resulting Java Callable Wrappers
contain `+` and not `/`:

	// Java Callable Wrapper
	/* partial */ class MauiApplication_ActivityLifecycleCallbacks
	{
	  public static final String __md_methods;
	  static {
	    __md_methods =
	      // …
	      "n_onActivityCreated:(Landroid/app/Activity;Landroid/os/Bundle;)V:GetOnActivityCreated_Landroid_app_Activity_Landroid_os_Bundle_Handler:Android.App.Application+IActivityLifecycleCallbacksInvoker, Mono.Android, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null\n" +
	      // …
	      "";
	    mono.android.Runtime.register ("Microsoft.Maui.MauiApplication+ActivityLifecycleCallbacks, Microsoft.Maui", MauiApplication_ActivityLifecycleCallbacks.class, __md_methods);
	  }
	}
jonpryor added a commit to dotnet/java-interop that referenced this pull request Feb 6, 2025
Context: dotnet/android#9747
Context: https://discord.com/channels/732297728826277939/732297837953679412/1336353039031734352
Context: https://discord.com/channels/732297728826277939/732297837953679412/1336358257769316372
Context: #1302
Context: dotnet/android#9750

The `[Register]` attribute provides "connector method" names, and for
interface methods this will also include the name of the type which
declares the method, which itself may be in a nested type:

	namespace Android.App {
	  public partial class Application {
	    public partial interface IActivityLifecycleCallbacks : IJavaObject, IJavaPeerable {
	      [Register (
	          name: "onActivityCreated",
	          signature: "(Landroid/app/Activity;Landroid/os/Bundle;)V",
	          connector: "GetOnActivityCreated_Landroid_app_Activity_Landroid_os_Bundle_Handler:Android.App.Application/IActivityLifecycleCallbacksInvoker, Mono.Android, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null")]
	      void OnActivityCreated (Android.App.Activity activity, Android.OS.Bundle? savedInstanceState);

	      // …
	    }
	  }
	}

The `connector` value is copied as-is into Java Callable Wrappers,
as part of the `__md_methods` value and `Runtime.register()` call.
Given the C# type:

	partial class MauiApplication : Application {
	  partial class ActivityLifecycleCallbacks : Java.Lang.Object, Application.IActivityLifecycleCallbacks {
	    public void OnActivityCreated (Activity activity, Bundle? savedInstanceState) => …
	  }
	}

then `Java.Interop.Tools.JavaCallableWrappers` will produce:

	// Java Callable Wrapper
	/* partial */ class MauiApplication_ActivityLifecycleCallbacks
	{
	  public static final String __md_methods;
	  static {
	    __md_methods =
	      // …
	      "n_onActivityCreated:(Landroid/app/Activity;Landroid/os/Bundle;)V:GetOnActivityCreated_Landroid_app_Activity_Landroid_os_Bundle_Handler:Android.App.Application/IActivityLifecycleCallbacksInvoker, Mono.Android, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null\n" +
	      // …
	      "";
	    mono.android.Runtime.register ("Microsoft.Maui.MauiApplication+ActivityLifecycleCallbacks, Microsoft.Maui", MauiApplication_ActivityLifecycleCallbacks.class, __md_methods);
	  }
	}

The `signature` and `connector` values from the `[Register(…)]` on
the method declaration are copied as-is into `__md_methods`.
As the `connector` value contains a `/`, `__md_methods` does as well.

This has worked fine for nearly 15+ years…on Mono/MonoVM.

This *fails* on NativeAOT and CoreCLR, as:

	Type.GetType ("Android.App.Application/IActivityLifecycleCallbacksInvoker, Mono.Android, …", throwOnError:true)

fails with:

	TypeLoadException: Could not resolve type 'Android.App.Application/IActivityLifecycleCallbacksInvoker' in assembly 'Mono.Android, …'.

The reason for the failure is that when using Reflection APIs such as
`Type.GetType()`, the [`Type.AssemblyQualifiedName` grammar][0] must
be followed, and that grammar uses `+` to "Precede a nested class",
*not* `/`.  (`/` isn't even in the Reflection grammar!)

(Aside: where does `/` come from then?  It's the *IL* separator for
nested types!)

For eventual CoreCLR and NativeAOT support, then, we need to replace
`/` with `+` *somewhere* before it hits `Type.GetType()`.

There are (at least?) three places to do so:

 1. Within `JniRuntime.JniTypeManager.RegisterNativeMembers()` or
    equivalent override.

 2. Within `generator`, updating the contents of `[Register]`.

 3. Within `Java.Interop.Tools.JavaCallableWrappers`.

(1) is rejected out of hand as it would be additional work done on-
device at runtime.  Why do that if we don't need to?

(2) was attempted in #1302 & dotnet/android#9750.
It turned into a bit of a boondoggle, because there are lots of
linker steps which interpret the `connector` value on `[Register]`,
and it "just worked" that the `connector` value contained IL names,
as the linker steps deal in IL!  Trying to update `connector` to
instead contain Reflection syntax required finding all the places in
the linker that used `connector` values, which was tedious & brittle.

"Just" (2) is also inadequate, as it would require new binding
assemblies to take advantage of, so (2) *also* needed (3).

Which brings us to (3), the current approach: *don't* alter the
semantics of the `connect` value within `[Register]`, and instead
require that `Java.Interop.Tools.JavaCallableWrappers` replace all
instances of `/` with `+` within `__md_methods`.  This is needed
*anyway*, for compatibility with existing bindings, and also avoids
lots of pain that (2) encountered.

With this approach, `generator` output is unchanged, and
`jcw-gen` output for `MauiApplication.ActivityLifecycleCallbacks`
becomes:

	// Java Callable Wrapper
	/* partial */ class MauiApplication_ActivityLifecycleCallbacks
	{
	  public static final String __md_methods;
	  static {
	    __md_methods =
	      // …
	      "n_onActivityCreated:(Landroid/app/Activity;Landroid/os/Bundle;)V:GetOnActivityCreated_Landroid_app_Activity_Landroid_os_Bundle_Handler:Android.App.Application+IActivityLifecycleCallbacksInvoker, Mono.Android, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null\n" +
	      // …
	      "";
	    mono.android.Runtime.register ("Microsoft.Maui.MauiApplication+ActivityLifecycleCallbacks, Microsoft.Maui", MauiApplication_ActivityLifecycleCallbacks.class, __md_methods);
	  }
	}

[0]: https://learn.microsoft.com/en-us/dotnet/api/system.type.assemblyqualifiedname?view=net-9.0#remarks
jonpryor added a commit that referenced this pull request Feb 7, 2025
Context: #9747

Changes: dotnet/java-interop@dd3c1d0...6bc87e8

  * dotnet/java-interop@6bc87e8b: [jcw-gen] Use `+` for nested types, not `/` (dotnet/java-interop#1304)

dotnet/java-interop@6bc87e8b is needed to unblock parts of #9747.

Additionally, two "quality of life" changes:

Firstly, update the `src/Mono.Android` build so that if there are
API breaks reported, the breakage is collated into 
`src/Mono.Android/ApiCompatLinesToAdd.txt` in a format that can be
directly copied into 
`tests/api-compatibility/acceptable-breakages-vReference-*.txt` if
deemed useful.

Previously, *all* changes were printed to the build log, which was
annoying to deal with because (1) there may be duplicates, and
(2) the lines would contain `TaskId`/etc. "noise" that would need to
be removed in order to be used.

Secondly, update `build-tools/xaprepare` to improve reliability when
running `Step_InstallDotNetPreview`.  `Step_InstallDotNetPreview`
will cache e.g. `dotnet-install.sh` into `$HOME/android-archives`,
but there was no logic to verify that it was still *valid*.

We've been seeing some recurring build failures in **macOS > Build**
such as:

	Downloading dotnet-install script...
	Warning: Using cached installation script found in '/Users/builder/android-archives/dotnet-install.sh'
	Discovering download URLs for dotnet SDK '10.0.100-preview.2.25102.3'...
	Downloading dotnet archive...
	dotnet archive URL https://dotnetcli.azureedge.net/dotnet/Sdk/10.0.100-preview.2.25102.3/dotnet-sdk-10.0.100-preview.2.25102.3-osx-arm64.tar.gz not found
	Downloading dotnet archive...
	dotnet archive URL https://dotnetcli.azureedge.net/dotnet/Sdk/10.0.100-preview.2.25102.3/dotnet-dev-osx-arm64.10.0.100-preview.2.25102.3.tar.gz not found
	Downloading dotnet archive...
	Warning: Failed to obtain dotnet archive size. HTTP status code: InternalServerError (500)
	Downloading dotnet archive...
	Warning: Failed to obtain dotnet archive size. HTTP status code: InternalServerError (500)
	  Error: Installation of dotnet SDK '10.0.100-preview.2.25102.3' failed.

	Step Xamarin.Android.Prepare.Step_InstallDotNetPreview failed
	System.InvalidOperationException: Step Xamarin.Android.Prepare.Step_InstallDotNetPreview failed
	   at Xamarin.Android.Prepare.Scenario.Run(Context context, Log log) in /Users/builder/azdo/_work/8/s/xamarin-android/build-tools/xaprepare/xaprepare/Application/Scenario.cs:line 50
	   at Xamarin.Android.Prepare.Context.Execute() in /Users/builder/azdo/_work/8/s/xamarin-android/build-tools/xaprepare/xaprepare/Application/Context.cs:line 488
	   at Xamarin.Android.Prepare.App.Run(String[] args) in /Users/builder/azdo/_work/8/s/xamarin-android/build-tools/xaprepare/xaprepare/Main.cs:line 155

Indeed, [`dotnet-sdk-10.0.100-preview.2.25102.3-osx-arm64.tar.gz`][0]
no longer exists on <https://dotnetcli.azureedge.net>.

The problem, though, is that .NET changed the CDN that is used in the
past month, and that's not the correct URL.  A newer `dotnet-install.sh`
reports:

	% bash "…/dotnet-install.sh" "--version" "10.0.100-preview.2.25102.3" "--install-dir" "/Volumes/Xamarin-Work/src/dotnet/android/bin/Release/dotnet" "--verbose" "--dry-run
	…
	dotnet-install: Link 0: primary, 10.0.100-preview.2.25102.3, https://builds.dotnet.microsoft.com/dotnet/Sdk/10.0.100-preview.2.25102.3/dotnet-sdk-10.0.100-preview.2.25102.3-osx-x64.tar.gz
	dotnet-install: Link 1: legacy, 10.0.100-preview.2.25102.3, https://builds.dotnet.microsoft.com/dotnet/Sdk/10.0.100-preview.2.25102.3/dotnet-dev-osx-x64.10.0.100-preview.2.25102.3.tar.gz
	dotnet-install: Link 2: primary, 10.0.100-preview.2.25102.3, https://ci.dot.net/public/Sdk/10.0.100-preview.2.25102.3/dotnet-sdk-10.0.100-preview.2.25102.3-osx-x64.tar.gz
	dotnet-install: Link 3: legacy, 10.0.100-preview.2.25102.3, https://ci.dot.net/public/Sdk/10.0.100-preview.2.25102.3/dotnet-dev-osx-x64.10.0.100-preview.2.25102.3.tar.gz

Note the different domain, <https://builds.dotnet.microsoft.com>!

Update `Step_InstallDotNetPreview` to try to install .NET potentially
*twice*: the first time using the cached `dotnet-install.sh`, and
*if that fails*, it tries again after downloading a *new*
`dotnet-install.sh`.

Hopefully this will fix the build failure on this machine!

[0]: https://dotnetcli.azureedge.net/dotnet/Sdk/10.0.100-preview.2.25102.3/dotnet-sdk-10.0.100-preview.2.25102.3-osx-arm64.tar.gz
@jonathanpeppers jonathanpeppers force-pushed the dev/peppers/maui/nativeaot branch from 1f83a81 to bd0d43c Compare February 7, 2025 16:39
jonpryor pushed a commit that referenced this pull request Feb 11, 2025
…9779)

Context: #9747

Calling `Android.App.Application.Context` currently reads the
`mono.MonoPackageManager.Context` field, which is set on startup by a
`ContentProvider`.  `mono.MonoPackageManager` does not exist in
NativeAOT (#9747), so let's move the field to the
`ApplicationRegistration` type, which *does* exist.
@jonathanpeppers jonathanpeppers mentioned this pull request Feb 12, 2025
11 tasks
@jonathanpeppers jonathanpeppers force-pushed the dev/peppers/maui/nativeaot branch from d6ee645 to e42a080 Compare February 12, 2025 16:28
jonpryor added a commit to dotnet/java-interop that referenced this pull request Feb 14, 2025
Context: dotnet/android#9747
Context: https://discord.com/channels/732297728826277939/732297837953679412/1339638847864176640
Context: https://discord.com/channels/732297728826277939/732297837953679412/1340011105510101063

While trying to get a MAUI sample running atop NativeAOT, we're
observing the following crash:

	E AndroidRuntime: net.dot.jni.internal.JavaProxyThrowable: System.InvalidCastException: Arg_InvalidCastException
	E AndroidRuntime:    at Java.Lang.Object._GetObject[T](IntPtr, JniHandleOwnership) + 0x64
	E AndroidRuntime:    at Microsoft.Maui.WindowOverlay.Initialize() + 0x168

Further investigation shows that the crash is from accessing the
`Activity.WindowManager` property:

	namespace Android.App;
	partial class Activity {
	  public virtual unsafe Android.Views.IWindowManager? WindowManager {
	    // Metadata.xml XPath method reference: path="/api/package[@name='android.app']/class[@name='Activity']/method[@name='getWindowManager' and count(parameter)=0]"
	    [Register ("getWindowManager", "()Landroid/view/WindowManager;", "GetGetWindowManagerHandler")]
	    get {
	      const string __id = "getWindowManager.()Landroid/view/WindowManager;";
	      try {
	        var __rm = _members.InstanceMethods.InvokeVirtualObjectMethod (__id, this, null);
	        return global::Java.Lang.Object.GetObject<Android.Views.IWindowManager> (__rm.Handle, JniHandleOwnership.TransferLocalRef);
	      } finally {
	      }
	    }
	  }
	}

`Object.GetObject<T>()` is now a wrapper around
`JniRuntime.JniValueManager.GetPeer()`, so the problem, rephrased,
is that this:

	var peer = JniEnvironment.Runtime.ValueManager.GetPeer (
	    ref h,
	    JniObjectReferenceOptions.CopyAndDispose,
	    targetType:typeof (IWindowManager));

returns a value which *does not implement* `IWindowManager`.
It was, in fact, returning a `Java.Lang.Object` instance (!).

The cause of the bug is that `JniRuntime.JniValueManager.CreatePeer()`
was not checking that the `Type` returned form
`Runtime.TypeManager.GetType(JniTypeSignature)` was compatible with
`targetType`; instead, it returned the first type in the inheritance
chain that had an activation constructor.  This was `Java.Lang.Object`.

Later, when `_GetObject<T>()` tried to cast the return value of
`JniRuntime.JniValueManager.GetPeer()` to `IWindowManager`, it failed.

Fix this by updating `CreatePeer()` to check that the `Type` from
`JniRuntime.JniTypeManager.GetType(JniTypeSignature)` is assignable
to `targetType`.  This ensures that we *don't* return a
`Java.Lang.Object` instance, allowing the cast to succeed.

Update `JniRuntimeJniValueManagerContract` to test these new semantics.
jonpryor added a commit to dotnet/java-interop that referenced this pull request Feb 15, 2025
Context: dotnet/android#9747
Context: https://discord.com/channels/732297728826277939/732297837953679412/1339638847864176640
Context: https://discord.com/channels/732297728826277939/732297837953679412/1340011105510101063

While trying to get a MAUI sample running atop NativeAOT, we're
observing the following crash:

	E AndroidRuntime: net.dot.jni.internal.JavaProxyThrowable: System.InvalidCastException: Arg_InvalidCastException
	E AndroidRuntime:    at Java.Lang.Object._GetObject[T](IntPtr, JniHandleOwnership) + 0x64
	E AndroidRuntime:    at Microsoft.Maui.WindowOverlay.Initialize() + 0x168

Further investigation shows that the crash is from accessing the
`Activity.WindowManager` property:

	namespace Android.App;
	partial class Activity {
	  public virtual unsafe Android.Views.IWindowManager? WindowManager {
	    // Metadata.xml XPath method reference: path="/api/package[@name='android.app']/class[@name='Activity']/method[@name='getWindowManager' and count(parameter)=0]"
	    [Register ("getWindowManager", "()Landroid/view/WindowManager;", "GetGetWindowManagerHandler")]
	    get {
	      const string __id = "getWindowManager.()Landroid/view/WindowManager;";
	      try {
	        var __rm = _members.InstanceMethods.InvokeVirtualObjectMethod (__id, this, null);
	        return global::Java.Lang.Object.GetObject<Android.Views.IWindowManager> (__rm.Handle, JniHandleOwnership.TransferLocalRef);
	      } finally {
	      }
	    }
	  }
	}

`Object.GetObject<T>()` is now a wrapper around
`JniRuntime.JniValueManager.GetPeer()`, so the problem, rephrased,
is that in this:

	var peer = JniEnvironment.Runtime.ValueManager.GetPeer (
	    ref h,
	    JniObjectReferenceOptions.CopyAndDispose,
	    targetType:typeof (IWindowManager));
	var wm   = (IWindowManager) peer;

`peer` is a value which *does not implement* `IWindowManager`.
It was, in fact, returning a `Java.Lang.Object` instance (!).
Consequently, the cast to `IWindowManager` throws.

The cause of the bug is that `JniRuntime.JniValueManager.CreatePeer()`
was not checking that the `Type` returned form
`Runtime.TypeManager.GetType(JniTypeSignature)` was compatible with
`targetType`; instead, it returned the first type in the inheritance
chain that had an activation constructor.  This was `Java.Lang.Object`.

Later, when `_GetObject<T>()` tried to cast the return value of
`JniRuntime.JniValueManager.GetPeer()` to `IWindowManager`, it failed.

Fix this by updating `CreatePeer()` to check that the `Type` from
`JniRuntime.JniTypeManager.GetType(JniTypeSignature)` is assignable
to `targetType`.  This ensures that we *don't* return a
`Java.Lang.Object` instance, allowing the cast to succeed.

Update `JniRuntimeJniValueManagerContract` to test these new semantics.
jonpryor added a commit that referenced this pull request Feb 15, 2025
@jonathanpeppers jonathanpeppers force-pushed the dev/peppers/maui/nativeaot branch from e42a080 to 846479a Compare February 18, 2025 14:44
jonpryor pushed a commit that referenced this pull request Feb 18, 2025
Context: 70bd636
Context: #9747
Context: dotnet/java-interop@2a7183a
Context: 7acf328

Commit 70bd636 introduced an "Minimum Viable Product" for a managed
typemap for NativeAOT.

Unfortunately, a `dotnet new maui` project template (#9747) still
crashed with:

	AndroidRuntime: Process: net.dot.hellonativeaot, PID: 16075
	AndroidRuntime: net.dot.jni.internal.JavaProxyThrowable: System.InvalidCastException: Arg_InvalidCastException
	AndroidRuntime:    at Java.Lang.Object._GetObject[T](IntPtr, JniHandleOwnership) + 0x64
	AndroidRuntime:    at Microsoft.Maui.WindowOverlay.Initialize() + 0x168
	AndroidRuntime:    at Microsoft.Maui.Handlers.WindowHandler.OnRootViewChanged(Object sender, EventArgs e) + 0x8c
	AndroidRuntime:    at Microsoft.Maui.Platform.NavigationRootManager.SetContentView(IView) + 0x17c
	AndroidRuntime:    at Microsoft.Maui.Platform.NavigationRootManager.Connect(IView, IMauiContext) + 0xf0
	AndroidRuntime:    at Microsoft.Maui.Handlers.WindowHandler.CreateRootViewFromContent(IWindowHandler, IWindow) + 0x4c
	AndroidRuntime:    at Microsoft.Maui.Handlers.WindowHandler.MapContent(IWindowHandler handler, IWindow window) + 0x20
	AndroidRuntime:    at Microsoft.Maui.PropertyMapper`2.<>c__DisplayClass5_0.<Add>b__0(IElementHandler h, IElement v) + 0x130
	AndroidRuntime:    at Microsoft.Maui.PropertyMapper.UpdatePropertyCore(String, IElementHandler, IElement) + 0x58
	AndroidRuntime:    at Microsoft.Maui.PropertyMapper.UpdateProperties(IElementHandler, IElement) + 0x74
	AndroidRuntime:    at Microsoft.Maui.Handlers.ElementHandler.SetVirtualView(IElement) + 0x15c
	AndroidRuntime:    at Microsoft.Maui.Controls.Element.SetHandler(IElementHandler) + 0x124
	AndroidRuntime:    at Microsoft.Maui.Controls.Element.set_Handler(IElementHandler value) + 0xc
	AndroidRuntime:    at Microsoft.Maui.Platform.ElementExtensions.SetHandler(Context, IElement, IMauiContext) + 0xfc
	AndroidRuntime:    at Microsoft.Maui.Platform.ApplicationExtensions.CreatePlatformWindow(Activity, IApplication, Bundle) + 0x184
	AndroidRuntime:    at Microsoft.Maui.MauiAppCompatActivity.OnCreate(Bundle) + 0x74
	AndroidRuntime:    at Android.App.Activity.n_OnCreate_Landroid_os_Bundle_(IntPtr jnienv, IntPtr native__this, IntPtr native_savedInstanceState) + 0xc0
	AndroidRuntime: at my.MainActivity.n_onCreate(Native Method)
	AndroidRuntime: at my.MainActivity.onCreate(MainActivity.java:36)
	AndroidRuntime: at android.app.Activity.performCreate(Activity.java:8960)
	AndroidRuntime: at android.app.Activity.performCreate(Activity.java:8938)
	AndroidRuntime: at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1526)
	AndroidRuntime: at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3975)
	AndroidRuntime: at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:4173)
	AndroidRuntime: at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:114)
	AndroidRuntime: at android.app.servertransaction.TransactionExecutor.executeNonLifecycleItem(TransactionExecutor.java:231)
	AndroidRuntime: at android.app.servertransaction.TransactionExecutor.executeTransactionItems(TransactionExecutor.java:152)
	AndroidRuntime: at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:93)
	AndroidRuntime: at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2595)
	AndroidRuntime: at android.os.Handler.dispatchMessage(Handler.java:107)
	AndroidRuntime: at android.os.Looper.loopOnce(Looper.java:232)
	AndroidRuntime: at android.os.Looper.loop(Looper.java:317)
	AndroidRuntime: at android.app.ActivityThread.main(ActivityThread.java:8592)
	AndroidRuntime: at java.lang.reflect.Method.invoke(Native Method)
	AndroidRuntime: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:580)
	AndroidRuntime: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:878)

The actual cause of the above `InvalidCastException` was fixed in
dotnet/java-interop@2a7183a1.

However, we also determined that the typemap associated
`java/lang/Object` with `Java.Interop.JavaObject`, *not*
to `Java.Lang.Object`, because 70bd636 processed `Java.Interop.dll`
before `Mono.Android.dll` and other assemblies.
(Related: commit 7acf328, which began treating `Java.Interop.dll` as
an assembly that could contain type mappings, and had to be special-
cased in order to ensure `java/lang/Object` was mapped to
`Java.Lang.Object, Mono.Android`.)

The typemap implementation was missing two edge cases:

 1. Types in `Mono.Android.dll` should be preferred over types in
    other assemblies.

 2. Abstract or interface types should be preferred over their
    equivalent `*Invoker` types.

There was also some missing logic regarding `*Invoker` types and
abstract types.

I fixed these cases in `TypeMappingStep.cs` and added a "test" that
can validates the typemap during a build.
jonpryor added a commit to dotnet/java-interop that referenced this pull request Feb 20, 2025
Context: dotnet/android#9747
Context: dotnet/android#9812

In the ongoing epic to get MAUI running atop NativeAOT, we hit our
longstanding NativeAOT nemesis: P/Invoke:

	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

(`JNIEnv.monodroid_typemap_managed_to_java()` is P/Invoke.)

The reasonable fix/workaround: update `JNIEnv.FindClass(Type)` to
instead use `JniRuntime.JniTypeManager.GetTypeSignature(Type)`.
(Also known as "embrace more JniRuntime abstractions!".)

Unfortunately, this straightforward idea hits a minor schism between
the .NET for Android and builtin java-interop world orders:

How should Java `byte` be bound?

For starters, what *is* a [Java `byte`][0]?

> The values of the integral types are integers in the following ranges:
>
>   * For `byte`, from -128 to 127, inclusive

The Java `byte` is *signed*!  Because of that, and because
java-interop originated as a Second System Syndrome rebuild of
Xamarin.Android, *of course* java-interop bound Java `byte` as
`System.SByte`.

.NET for Android, though, bound Java `byte` as `System.Byte`.

This "minor" change meant that lots of unit tests started failing, e.g.
[`NetworkInterfacesTest.DotNetInterfacesShouldEqualJavaInterfaces()`][2]:

	System.ArgumentException : Could not determine Java type corresponding to System.Byte[], System.Private.CoreLib, Version=10.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e. Arg_ParamName_Name, type
	   at Android.Runtime.JNIEnv.FindClass(Type )
	   at Android.Runtime.JNIEnv.AssertCompatibleArrayTypes(IntPtr , Type )
	   at Android.Runtime.JNIEnv._GetArray(IntPtr , Type )
	   at Android.Runtime.JNIEnv.GetArray(IntPtr , JniHandleOwnership , Type )
	   at Java.Net.NetworkInterface.GetHardwareAddress()
	   at System.NetTests.NetworkInterfacesTest.GetInfos(IEnumeration interfaces)
	   at System.NetTests.NetworkInterfacesTest.DotNetInterfacesShouldEqualJavaInterfaces()
	   at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
	   at System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(Object , BindingFlags )

Rephrased, `runtime.TypeManager.GetTypeSignature(typeof(byte[]))`
returned a "default" `JniTypeSignature` instance.

It's time to reduce the size of this schism.

Update `JniBuiltinMarshalers.GetBuiltInTypeSignature()` so that
`TypeCode.Byte` is treated as a synonym for `TypeCode.SByte`.
This is in fact all that's needed in order to add support for `byte[]`!

It's *not* all that's necessary to fix all unit tests.

Update `JniRuntime.JniTypeManager.GetTypeSignature()` and
`.GetTypeSignatures()` so that if the type is an open generic type
a `System.NotSupportedException` is thrown instead of a
`System.ArgumentException`.  This fixes
[`Java.InteropTests.JnienvTest.NewOpenGenericTypeThrows()`][3].

Also, `JniBuiltinMarshalers.cs` got some hand-made changes, rendering
it out of sync with `JniBuiltinMarshalers.tt`.  Regenerate it.

[0]: https://docs.oracle.com/javase/specs/jls/se21/html/jls-4.html#jls-4.2.1
[1]: https://github.com/dotnet/java-interop/blob/f30e420a1638dc013302e85dcf76642c10c26832/Documentation/Motivation.md
[2]: https://github.com/dotnet/android/blob/1b1f1452f6b05707418d6605c06e106e6a2a6381/tests/Mono.Android-Tests/Mono.Android-Tests/System.Net/NetworkInterfaces.cs#L107-L137
[3]: https://github.com/dotnet/android/blob/1b1f1452f6b05707418d6605c06e106e6a2a6381/tests/Mono.Android-Tests/Mono.Android-Tests/Java.Interop/JnienvTest.cs#L107-L116
jonpryor added a commit to dotnet/java-interop that referenced this pull request Feb 21, 2025
Context: dotnet/android#9747
Context: dotnet/android#9812
Context: 71afce5
Context: dotnet/android@aa5e597
Context: dotnet/android@f800c1a

In the ongoing epic to get MAUI running atop NativeAOT, we hit our
longstanding NativeAOT nemesis: a P/Invoke:

	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

(`JNIEnv.monodroid_typemap_managed_to_java()` is P/Invoke.  Why are
P/Invokes bad?  See dotnet/android@f800c1a6.)

The reasonable fix/workaround: update `JNIEnv.FindClass(Type)` to
instead use `JniRuntime.JniTypeManager.GetTypeSignature(Type)`.
(Also known as "embrace more JniRuntime abstractions!".)

Unfortunately, this straightforward idea hits a minor schism between
the .NET for Android and builtin java-interop world orders:

How should Java `byte` be bound?

For starters, what *is* a [Java `byte`][0]?

> The values of the integral types are integers in the following ranges:
>
>   * For `byte`, from -128 to 127, inclusive

The Java `byte` is *signed*!  Because of that, and because
java-interop originated as a Second System Syndrome rebuild of
Xamarin.Android, *of course* java-interop bound Java `byte` as
`System.SByte`.

.NET for Android, though, bound Java `byte` as `System.Byte`.

This "minor" change meant that lots of unit tests started failing, e.g.
[`NetworkInterfacesTest.DotNetInterfacesShouldEqualJavaInterfaces()`][2]:

	System.ArgumentException : Could not determine Java type corresponding to System.Byte[], System.Private.CoreLib, Version=10.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e. Arg_ParamName_Name, type
	   at Android.Runtime.JNIEnv.FindClass(Type )
	   at Android.Runtime.JNIEnv.AssertCompatibleArrayTypes(IntPtr , Type )
	   at Android.Runtime.JNIEnv._GetArray(IntPtr , Type )
	   at Android.Runtime.JNIEnv.GetArray(IntPtr , JniHandleOwnership , Type )
	   at Java.Net.NetworkInterface.GetHardwareAddress()
	   at System.NetTests.NetworkInterfacesTest.GetInfos(IEnumeration interfaces)
	   at System.NetTests.NetworkInterfacesTest.DotNetInterfacesShouldEqualJavaInterfaces()
	   at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
	   at System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(Object , BindingFlags )

Rephrased, `runtime.TypeManager.GetTypeSignature(typeof(byte[]))`
returned a "default" `JniTypeSignature` instance.

It's time to reduce the size of this schism.

Update `JniBuiltinMarshalers.GetBuiltInTypeSignature()` so that
`TypeCode.Byte` is treated as a synonym for `TypeCode.SByte`.
This is in fact all that's needed in order to add support for `byte[]`!

Repeat this exercise for all other unsigned types: `ushort`, `uint`,
and `ulong`, as Kotlin unsigned types require it; see also 71afce5
and dotnet/android@aa5e597eba.  This fixes the exception:

	System.InvalidCastException : Unable to cast from '[I' to '[Ljava/lang/Object;'.
	   at Android.Runtime.JNIEnv.AssertCompatibleArrayTypes(IntPtr , Type )
	   at Android.Runtime.JNIEnv._GetArray(IntPtr , Type )
	   at Android.Runtime.JNIEnv.GetArray(IntPtr , JniHandleOwnership , Type )
	   at Foo.UnsignedInstanceMethods.UintArrayInstanceMethod(UInt32[] value)
	   at Xamarin.Android.JcwGenTests.KotlinUnsignedTypesTests.TestUnsignedArrayTypeMembers()
	   at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
	   at System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(Object , BindingFlags )

This is *not* all that's necessary to fix all dotnet/android tests.

Update `JniRuntime.JniTypeManager.GetTypeSignature()` and
`.GetTypeSignatures()` so that if the type is an open generic type
a `System.NotSupportedException` is thrown instead of a
`System.ArgumentException`.  This fixes
[`Java.InteropTests.JnienvTest.NewOpenGenericTypeThrows()`][3].

Also, `JniBuiltinMarshalers.cs` has some hand-made changes, rendering
it out of sync with `JniBuiltinMarshalers.tt`.  Update
`JniBuiltinMarshalers.tt` appropriately and regenerate it.

[0]: https://docs.oracle.com/javase/specs/jls/se21/html/jls-4.html#jls-4.2.1
[1]: https://github.com/dotnet/java-interop/blob/f30e420a1638dc013302e85dcf76642c10c26832/Documentation/Motivation.md
[2]: https://github.com/dotnet/android/blob/1b1f1452f6b05707418d6605c06e106e6a2a6381/tests/Mono.Android-Tests/Mono.Android-Tests/System.Net/NetworkInterfaces.cs#L107-L137
[3]: https://github.com/dotnet/android/blob/1b1f1452f6b05707418d6605c06e106e6a2a6381/tests/Mono.Android-Tests/Mono.Android-Tests/Java.Interop/JnienvTest.cs#L107-L116
@jonathanpeppers jonathanpeppers force-pushed the dev/peppers/maui/nativeaot branch from 69e3c76 to 86d52fc Compare February 21, 2025 16:47
jonpryor pushed a commit that referenced this pull request Feb 21, 2025
Context: #9747

A .NET MAUI + NativeAOT project fails with:

	E AndroidRuntime: net.dot.jni.internal.JavaProxyThrowable: System.DllNotFoundException: DllNotFound_Linux, xa-internal-api,
	E AndroidRuntime: dlopen failed: library "xa-internal-api.so" not found
	E AndroidRuntime: dlopen failed: library "libxa-internal-api.so" not found
	E AndroidRuntime: dlopen failed: library "xa-internal-api" not found
	E AndroidRuntime: dlopen failed: library "libxa-internal-api" not found
	E AndroidRuntime:
	E AndroidRuntime:    at System.Runtime.InteropServices.NativeLibrary.LoadLibErrorTracker.Throw(String) + 0x4c
	E AndroidRuntime:    at Internal.Runtime.CompilerHelpers.InteropHelpers.FixupModuleCell(InteropHelpers.ModuleFixupCell*) + 0xe4
	E AndroidRuntime:    at Internal.Runtime.CompilerHelpers.InteropHelpers.ResolvePInvokeSlow(InteropHelpers.MethodFixupCell*) + 0x40
	E AndroidRuntime:    at Android.Runtime.RuntimeNativeMethods.monodroid_TypeManager_get_java_class_name(IntPtr) + 0x2c
	E AndroidRuntime:    at Java.Interop.TypeManager.GetClassName(IntPtr) + 0x10
	E AndroidRuntime:    at Android.Runtime.JNIEnv.GetClassNameFromInstance(IntPtr) + 0x1c
	E AndroidRuntime:    at Android.Runtime.JNIEnv.GetConverter[TValue](Dictionary`2, Type, IntPtr) + 0x6c
	E AndroidRuntime:    at Android.Runtime.JNIEnv.CopyArray[T](IntPtr, T[]) + 0x90
	E AndroidRuntime:    at Android.Graphics.Drawables.LayerDrawable..ctor(Drawable[] layers) + 0x1a4
	E AndroidRuntime:    at Microsoft.Maui.Platform.MauiRippleDrawableExtensions.UpdateMauiRippleDrawableBackground(View, Paint, IButtonStroke, Func`1, Func`1, Action) + 0x2ac
	E AndroidRuntime:    at Microsoft.Maui.PropertyMapper.UpdateProperties(IElementHandler, IElement) + 0x64
	E AndroidRuntime:    at Microsoft.Maui.Controls.Element.SetHandler(IElementHandler) + 0x124
	E AndroidRuntime:    at Microsoft.Maui.Platform.ElementExtensions.ToHandler(IElement, IMauiContext) + 0x160
	E AndroidRuntime:    at Microsoft.Maui.Platform.ElementExtensions.ToPlatform(IElement, IMauiContext) + 0x28
	E AndroidRuntime:    at Microsoft.Maui.Handlers.LayoutHandler.SetVirtualView(IView) + 0xd4
	E AndroidRuntime:    at Microsoft.Maui.Controls.Element.SetHandler(IElementHandler) + 0x124
	E AndroidRuntime:    at Microsoft.Maui.Platform.ElementExtensions.ToHandler(IElement, IMauiContext) + 0x160
	E AndroidRuntime:    at Microsoft.Maui.Platform.ElementExtensions.ToPlatform(IElement, IMauiContext) + 0x28
	E AndroidRuntime:    at Microsoft.Maui.Handlers.ScrollViewHandler.UpdateInsetView(IScrollView, IScrollViewHandler, ICrossPlatformLayout) + 0x34
	E AndroidRuntime:    at Microsoft.Maui.PropertyMapper.UpdateProperties(IElementHandler, IElement) + 0x64
	E AndroidRuntime:    at Microsoft.Maui.Controls.Element.SetHandler(IElementHandler) + 0x124
	E AndroidRuntime:    at Microsoft.Maui.Platform.ElementExtensions.ToHandler(IElement, IMauiContext) + 0x160
	E AndroidRuntime:    at Microsoft.Maui.Platform.ElementExtensions.ToPlatform(IElement, IMauiContext) + 0x28
	E AndroidRuntime:    at Microsoft.Maui.Handlers.ContentViewHandler.UpdateContent(IContentViewHandler) + 0xb4
	E AndroidRuntime:    at Microsoft.Maui.PropertyMapper.UpdateProperties(IElementHandler, IElement) + 0x64
	E AndroidRuntime:    at Microsoft.Maui.Handlers.ContentViewHandler.SetVirtualView(IView) + 0x18
	E AndroidRuntime:    at Microsoft.Maui.Controls.Element.SetHandler(IElementHandler) + 0x124
	E AndroidRuntime:    at Microsoft.Maui.Platform.ElementExtensions.ToHandler(IElement, IMauiContext) + 0x160
	E AndroidRuntime:    at Microsoft.Maui.Platform.ElementExtensions.ToPlatform(IElement, IMauiContext) + 0x28
	E AndroidRuntime:    at Microsoft.Maui.Controls.Platform.Compatibility.ShellFragmentContainer.OnCreateView(LayoutInflater, ViewGroup, Bundle) + 0x64
	E AndroidRuntime:    at AndroidX.Fragment.App.Fragment.n_OnCreateView_Landroid_view_LayoutInflater_Landroid_view_ViewGroup_Landroid_os_Bundle_(IntPtr jnienv, IntPtr native__this, IntPtr native_inflater, IntPtr native_container, IntPtr native_savedInstanceState) + 0x104
	E AndroidRuntime: 	at crc640ec207abc449b2ca.ShellFragmentContainer.n_onCreateView(Native Method)
	E AndroidRuntime: 	at crc640ec207abc449b2ca.ShellFragmentContainer.onCreateView(ShellFragmentContainer.java:38)
	E AndroidRuntime: 	at androidx.fragment.app.Fragment.performCreateView(Fragment.java:3119)
	E AndroidRuntime: 	at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:577)
	E AndroidRuntime: 	at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:286)
	E AndroidRuntime: 	at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:2214)
	E AndroidRuntime: 	at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:2109)
	E AndroidRuntime: 	at androidx.fragment.app.FragmentManager.execSingleAction(FragmentManager.java:2002)
	E AndroidRuntime: 	at androidx.fragment.app.BackStackRecord.commitNow(BackStackRecord.java:317)
	E AndroidRuntime: 	at androidx.viewpager2.adapter.FragmentStateAdapter.placeFragmentInViewHolder(FragmentStateAdapter.java:342)
	E AndroidRuntime: 	at androidx.viewpager2.adapter.FragmentStateAdapter.onViewAttachedToWindow(FragmentStateAdapter.java:273)
	E AndroidRuntime: 	at androidx.viewpager2.adapter.FragmentStateAdapter.onViewAttachedToWindow(FragmentStateAdapter.java:73)
	E AndroidRuntime: 	at androidx.recyclerview.widget.RecyclerView.dispatchChildAttached(RecyclerView.java:8377)
	E AndroidRuntime: 	at androidx.recyclerview.widget.RecyclerView$5.addView(RecyclerView.java:954)
	E AndroidRuntime: 	at androidx.recyclerview.widget.ChildHelper.addView(ChildHelper.java:131)
	E AndroidRuntime: 	at androidx.recyclerview.widget.RecyclerView$LayoutManager.addViewInt(RecyclerView.java:9430)
	E AndroidRuntime: 	at androidx.recyclerview.widget.RecyclerView$LayoutManager.addView(RecyclerView.java:9388)
	E AndroidRuntime: 	at androidx.recyclerview.widget.RecyclerView$LayoutManager.addView(RecyclerView.java:9375)
	E AndroidRuntime: 	at androidx.recyclerview.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1676)
	E AndroidRuntime: 	at androidx.recyclerview.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1622)
	E AndroidRuntime: 	at androidx.recyclerview.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:687)
	E AndroidRuntime: 	at androidx.recyclerview.widget.RecyclerView.dispatchLayoutStep2(RecyclerView.java:4645)
	E AndroidRuntime: 	at androidx.recyclerview.widget.RecyclerView.dispatchLayout(RecyclerView.java:4348)
	E AndroidRuntime: 	at androidx.recyclerview.widget.RecyclerView.onLayout(RecyclerView.java:4919)
	E AndroidRuntime: 	at android.view.View.layout(View.java:25159)
	E AndroidRuntime: 	at android.view.ViewGroup.layout(ViewGroup.java:6460)
	E AndroidRuntime: 	at androidx.viewpager2.widget.ViewPager2.onLayout(ViewPager2.java:535)
	E AndroidRuntime: 	at android.view.View.layout(View.java:25159)
	E AndroidRuntime: 	at android.view.ViewGroup.layout(ViewGroup.java:6460)
	E AndroidRuntime: 	at com.google.android.material.appbar.HeaderScrollingViewBehavior.layoutChild(HeaderScrollingViewBehavior.java:149)
	E AndroidRuntime: 	at com.google.android.material.appbar.ViewOffsetBehavior.onLayoutChild(ViewOffsetBehavior.java:43)
	E AndroidRuntime: 	at com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior.onLayoutChild(AppBarLayout.java:2365)
	E AndroidRuntime: 	at androidx.coordinatorlayout.widget.CoordinatorLayout.onLayout(CoordinatorLayout.java:953)
	E AndroidRuntime: 	at android.view.View.layout(View.java:25159)
	E AndroidRuntime: 	at android.view.ViewGroup.layout(ViewGroup.java:6460)
	E AndroidRuntime: 	at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332)
	E AndroidRuntime: 	at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
	E AndroidRuntime: 	at android.view.View.layout(View.java:25159)
	E AndroidRuntime: 	at android.view.ViewGroup.layout(ViewGroup.java:6460)
	E AndroidRuntime: 	at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1891)
	E AndroidRuntime: 	at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1729)
	E AndroidRuntime: 	at android.widget.LinearLayout.onLayout(LinearLayout.java:1638)
	E AndroidRuntime: 	at android.view.View.layout(View.java:25159)
	E AndroidRuntime: 	at android.view.ViewGroup.layout(ViewGroup.java:6460)
	E AndroidRuntime: 	at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332)
	E AndroidRuntime: 	at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
	E AndroidRuntime: 	at android.view.View.layout(View.java:25159)
	E AndroidRuntime: 	at android.view.ViewGroup.layout(ViewGroup.java:6460)
	E AndroidRuntime: 	at androidx.drawerlayout.widget.DrawerLayout.onLayout(DrawerLayout.java:1273)
	E AndroidRuntime: 	at android.view.View.layout(View.java:25159)
	E AndroidRuntime: 	at android.view.ViewGroup.layout(ViewGroup.java:6460)
	E AndroidRuntime: 	at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1891)
	E AndroidRuntime: 	at android.widget.LinearLayout.layoutHorizontal(LinearLayout.java:1880)
	E AndroidRuntime: 	at android.widget.LinearLayout.onLayout(LinearLayout.java:1640)
	E AndroidRuntime: 	at android.view.View.layout(View.java:25159)
	E AndroidRuntime: 	at android.view.ViewGroup.layout(ViewGroup.java:6460)
	E AndroidRuntime: 	at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332)
	E AndroidRuntime: 	at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
	E AndroidRuntime: 	at android.view.View.layout(View.java:25159)
	E AndroidRuntime: 	at android.view.ViewGroup.layout(ViewGroup.java:6460)
	E AndroidRuntime: 	at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332)
	E AndroidRuntime: 	at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
	E AndroidRuntime: 	at android.view.View.layout(View.java:25159)
	E AndroidRuntime: 	at android.view.ViewGroup.layout(ViewGroup.java:6460)
	E AndroidRuntime: 	at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332)
	E AndroidRuntime: 	at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
	E AndroidRuntime: 	at android.view.View.layout(View.java:25159)
	E AndroidRuntime: 	at android.view.ViewGroup.layout(ViewGroup.java:6460)
	E AndroidRuntime: 	at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1891)
	E AndroidRuntime: 	at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1729)
	E AndroidRuntime: 	at android.widget.LinearLayout.onLayout(LinearLayout.java:1638)
	E AndroidRuntime: 	at android.view.View.layout(View.java:25159)
	E AndroidRuntime: 	at android.view.ViewGroup.layout(ViewGroup.java:6460)
	E AndroidRuntime: 	at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332)
	E AndroidRuntime: 	at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
	E AndroidRuntime: 	at com.android.internal.policy.DecorView.onLayout(DecorView.java:807)
	E AndroidRuntime: 	at android.view.View.layout(View.java:25159)
	E AndroidRuntime: 	at android.view.ViewGroup.layout(ViewGroup.java:6460)
	E AndroidRuntime: 	at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:4562)
	E AndroidRuntime: 	at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:3830)
	E AndroidRuntime: 	at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:2718)
	E AndroidRuntime: 	at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:9937)
	E AndroidRuntime: 	at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1406)
	E AndroidRuntime: 	at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1415)
	E AndroidRuntime: 	at android.view.Choreographer.doCallbacks(Choreographer.java:1015)
	E AndroidRuntime: 	at android.view.Choreographer.doFrame(Choreographer.java:945)
	E AndroidRuntime: 	at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1389)
	E AndroidRuntime: 	at android.os.Handler.handleCallback(Handler.java:959)
	E AndroidRuntime: 	at android.os.Handler.dispatchMessage(Handler.java:100)
	E AndroidRuntime: 	at android.os.Looper.loopOnce(Looper.java:232)
	E AndroidRuntime: 	at android.os.Looper.loop(Looper.java:317)
	E AndroidRuntime: 	at android.app.ActivityThread.main(ActivityThread.java:8592)
	E AndroidRuntime: 	at java.lang.reflect.Method.invoke(Native Method)
	E AndroidRuntime: 	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:580)
	E AndroidRuntime: 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:878)

Update `Android.Runtime.JNIEnv.GetClassNameFromInstance()` to call
`JniEnvironment.Types.GetJniTypeNameFromInstance()` instead of
`Java.Interop.TypeManager.GetClassName()`.  This avoids the P/Invoke,
fixing the `DllNotFoundException`.
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
do-not-merge PR should not be merged.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants