Skip to content

[Mono.Android] add fallback for TypemapManagedToJava #9811

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

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions samples/NativeAOT/MainActivity.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using Android.Content.Res;
using Android.Runtime;
using Android.Util;
using System.Reflection;
Expand All @@ -17,5 +18,9 @@ protected override void OnCreate(Bundle? savedInstanceState)

// Set our view from the "main" layout resource
SetContentView(Resource.Layout.activity_main);

// An example of an Android API that uses a Java array
var list = new ColorStateList (new int[][] { [ 0, 1 ]}, [0, 1]);
Log.Debug ("NativeAOT", "MainActivity.OnCreate() ColorStateList: " + list);
}
}
2 changes: 0 additions & 2 deletions src/Mono.Android/Android.App/SyncContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@ static bool EnsureLooper ([NotNullWhen (true)]Looper? looper, SendOrPostCallback
{
if (looper == null) {
var message = $"No Android message loop is available. Skipping invocation of `{d.Method.DeclaringType?.FullName}.{d.Method.Name}`!";
if (JNIEnvInit.IsRunningOnDesktop)
message += " Using `await` when running on the Android Designer is not currently supported. Please use the `View.IsInEditMode` property.";
Logger.Log (LogLevel.Error, "monodroid-synccontext", message);
return false;
}
Expand Down
16 changes: 9 additions & 7 deletions src/Mono.Android/Android.Runtime/AndroidRuntime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -278,30 +278,32 @@ protected override IEnumerable<Type> GetTypesForSimpleReference (string jniSimpl

protected override string? GetSimpleReference (Type type)
{
if (RuntimeFeature.UseReflectionForManagedToJava) {
return JavaNativeTypeManager.ToJniName (type);
}
Comment on lines +281 to +283
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
Contributor

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.

string? j = JNIEnv.TypemapManagedToJava (type);
if (j != null) {
return GetReplacementTypeCore (j) ?? j;
}
if (JNIEnvInit.IsRunningOnDesktop) {
return JavaNativeTypeManager.ToJniName (type);
}
return null;
}

protected override IEnumerable<string> GetSimpleReferences (Type type)
{
string? j = JNIEnv.TypemapManagedToJava (type);
j = GetReplacementTypeCore (j) ?? j;

if (JNIEnvInit.IsRunningOnDesktop) {
string? j;
if (RuntimeFeature.UseReflectionForManagedToJava) {
string? d = JavaNativeTypeManager.ToJniName (type);
j = GetReplacementTypeCore (d);
if (j != null && d != null) {
return new[]{j, d};
}
if (d != null) {
return new[]{d};
}
}

j = JNIEnv.TypemapManagedToJava (type);
j = GetReplacementTypeCore (j) ?? j;
if (j != null) {
return new[]{j};
}
Expand Down
3 changes: 3 additions & 0 deletions src/Mono.Android/Android.Runtime/JNIEnv.cs
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,9 @@ public static string GetJniName (Type type)
if (type == null)
throw new ArgumentNullException ("type");

if (RuntimeFeature.UseReflectionForManagedToJava)
return JavaNativeTypeManager.ToJniName (type);

string? java = TypemapManagedToJava (type);
return java == null
? JavaNativeTypeManager.ToJniName (type)
Expand Down
11 changes: 0 additions & 11 deletions src/Mono.Android/Android.Runtime/JNIEnvInit.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ internal struct JnienvInitializeArgs {
public int version; // TODO: remove, not needed anymore
public int grefGcThreshold;
public IntPtr grefIGCUserPeer;
public int isRunningOnDesktop;
public byte brokenExceptionTransitions;
public int packageNamingPolicy;
public byte ioExceptionType;
Expand All @@ -36,7 +35,6 @@ internal struct JnienvInitializeArgs {
#pragma warning restore 0649

internal static JniRuntime.JniValueManager? ValueManager;
internal static bool IsRunningOnDesktop;
internal static bool jniRemappingInUse;
internal static bool MarshalMethodsEnabled;
internal static bool PropagateExceptions;
Expand Down Expand Up @@ -102,21 +100,12 @@ internal static unsafe void Initialize (JnienvInitializeArgs* args)
androidRuntime = new AndroidRuntime (args->env, args->javaVm, args->grefLoader, args->Loader_loadClass, args->jniAddNativeMethodRegistrationAttributePresent != 0);
ValueManager = androidRuntime.ValueManager;

IsRunningOnDesktop = args->isRunningOnDesktop == 1;

grefIGCUserPeer_class = args->grefIGCUserPeer;
grefGCUserPeerable_class = args->grefGCUserPeerable;

PropagateExceptions = args->brokenExceptionTransitions == 0;

JavaNativeTypeManager.PackageNamingPolicy = (PackageNamingPolicy)args->packageNamingPolicy;
if (IsRunningOnDesktop) {
var packageNamingPolicy = Environment.GetEnvironmentVariable ("__XA_PACKAGE_NAMING_POLICY__");
if (Enum.TryParse (packageNamingPolicy, out PackageNamingPolicy pnp)) {
JavaNativeTypeManager.PackageNamingPolicy = pnp;
}
}

SetSynchronizationContext ();
}

Expand Down
13 changes: 13 additions & 0 deletions src/Mono.Android/Android.Runtime/RuntimeFeature.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System;
using System.Diagnostics.CodeAnalysis;

namespace Android.Runtime;

static class RuntimeFeature
{
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
Contributor

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.

AppContext.TryGetSwitch ($"{FeatureSwitchPrefix}{nameof (UseReflectionForManagedToJava)}", out bool isEnabled) ? isEnabled : false;
}
4 changes: 4 additions & 0 deletions src/Mono.Android/ILLink/ILLink.Substitutions.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,9 @@
<method signature="System.Boolean get_NegotiateAuthenticationIsEnabled()" body="stub" feature="Xamarin.Android.Net.UseNegotiateAuthentication" featurevalue="false" value="false" />
<method signature="System.Boolean get_NegotiateAuthenticationIsEnabled()" body="stub" feature="Xamarin.Android.Net.UseNegotiateAuthentication" featurevalue="true" value="true" />
</type>
<type fullname="Android.Runtime.RuntimeFeature">
<method signature="System.Boolean get_UseReflectionForManagedToJava()" body="stub" feature="Android.Runtime.RuntimeFeature.UseReflectionForManagedToJava" featurevalue="false" value="false" />
<method signature="System.Boolean get_UseReflectionForManagedToJava()" body="stub" feature="Android.Runtime.RuntimeFeature.UseReflectionForManagedToJava" featurevalue="true" value="true" />
</type>
</assembly>
</linker>
12 changes: 5 additions & 7 deletions src/Mono.Android/Java.Interop/TypeManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -221,12 +221,10 @@ static Exception CreateJavaLocationException ()
if (type != null)
return type;

if (!JNIEnvInit.IsRunningOnDesktop) {
// Miss message is logged in the native runtime
if (Logger.LogAssembly)
JNIEnv.LogTypemapTrace (new System.Diagnostics.StackTrace (true));
return null;
}
// Miss message is logged in the native runtime
if (Logger.LogAssembly)
JNIEnv.LogTypemapTrace (new System.Diagnostics.StackTrace (true));
return null;

return null;
}
Expand Down Expand Up @@ -368,7 +366,7 @@ public static void RegisterType (string java_class, Type t)
if (String.Compare (jniFromType, java_class, StringComparison.OrdinalIgnoreCase) != 0) {
TypeManagerMapDictionaries.ManagedToJni.Add (t, java_class);
}
} else if (!JNIEnvInit.IsRunningOnDesktop || t != typeof (Java.Interop.TypeManager)) {
} else if (t != typeof (Java.Interop.TypeManager)) {
// skip the registration and output a warning
Logger.Log (LogLevel.Warn, "monodroid", FormattableString.Invariant ($"Type Registration Skipped for {java_class} to {t} "));
}
Expand Down
1 change: 1 addition & 0 deletions src/Mono.Android/Mono.Android.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@
<Compile Include="Android.Runtime\RequiresPermissionAttribute.cs" />
<Compile Include="Android.Runtime\ResourceDesignerAttribute.cs" />
<Compile Include="Android.Runtime\RuntimeConstants.cs" />
<Compile Include="Android.Runtime\RuntimeFeature.cs" />
<Compile Include="Android.Runtime\ResourceIdManager.cs" />
<Compile Include="Android.Runtime\RuntimeNativeMethods.cs" />
<Compile Include="Android.Runtime\StringDefAttribute.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.

<!-- NativeAOT's targets currently gives an error about cross-compilation -->
<DisableUnsupportedError Condition=" $([MSBuild]::IsOSPlatform('windows')) and '$(DisableUnsupportedError)' == '' ">true</DisableUnsupportedError>
</PropertyGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ See: https://github.com/dotnet/runtime/blob/b13715b6984889a709ba29ea8a1961db469f
Value="$(AndroidAvoidEmitForPerformance)"
Trim="true"
/>
<RuntimeHostConfigurationOption Include="Android.Runtime.RuntimeFeature.UseReflectionForManagedToJava"
Value="$([MSBuild]::ValueOrDefault('$(_AndroidUseReflectionForManagedToJava)', 'false'))"
Trim="true"
/>
</ItemGroup>

<Target Name="_ParseRuntimeConfigFiles"
Expand Down
1 change: 0 additions & 1 deletion src/native/mono/monodroid/monodroid-glue-internal.hh
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,6 @@ namespace xamarin::android::internal
int version;
int grefGcThreshold;
jobject grefIGCUserPeer;
int isRunningOnDesktop;
uint8_t brokenExceptionTransitions;
int packageNamingPolicy;
uint8_t boundExceptionType;
Expand Down
1 change: 0 additions & 1 deletion src/native/mono/monodroid/monodroid-glue.cc
Original file line number Diff line number Diff line change
Expand Up @@ -842,7 +842,6 @@ MonodroidRuntime::init_android_runtime (JNIEnv *env, jclass runtimeClass, jobjec
init.env = env;
init.logCategories = log_categories;
init.version = env->GetVersion ();
init.isRunningOnDesktop = is_running_on_desktop ? 1 : 0;
init.brokenExceptionTransitions = application_config.broken_exception_transitions ? 1 : 0;
init.packageNamingPolicy = static_cast<int>(application_config.package_naming_policy);
init.boundExceptionType = application_config.bound_exception_type;
Expand Down
Loading