Skip to content

[nativeaot] introduce Microsoft.Android.Runtime.NativeAOT.dll #9760

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

Merged
merged 7 commits into from
Feb 12, 2025
Merged
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
7 changes: 7 additions & 0 deletions Xamarin.Android.sln
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Android.Sdk.Analy
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "proguard-android", "src\proguard-android\proguard-android.csproj", "{5FD0133B-69E5-4474-9B67-9FD1D0150C70}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Android.Runtime.NativeAOT", "src\Microsoft.Android.Runtime.NativeAOT\Microsoft.Android.Runtime.NativeAOT.csproj", "{E8831F32-11D7-D42C-E43C-711998BC357A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|AnyCPU = Debug|AnyCPU
Expand Down Expand Up @@ -347,6 +349,10 @@ Global
{5FD0133B-69E5-4474-9B67-9FD1D0150C70}.Debug|AnyCPU.Build.0 = Debug|Any CPU
{5FD0133B-69E5-4474-9B67-9FD1D0150C70}.Release|AnyCPU.ActiveCfg = Release|Any CPU
{5FD0133B-69E5-4474-9B67-9FD1D0150C70}.Release|AnyCPU.Build.0 = Release|Any CPU
{E8831F32-11D7-D42C-E43C-711998BC357A}.Debug|AnyCPU.ActiveCfg = Debug|Any CPU
{E8831F32-11D7-D42C-E43C-711998BC357A}.Debug|AnyCPU.Build.0 = Debug|Any CPU
{E8831F32-11D7-D42C-E43C-711998BC357A}.Release|AnyCPU.ActiveCfg = Release|Any CPU
{E8831F32-11D7-D42C-E43C-711998BC357A}.Release|AnyCPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -406,6 +412,7 @@ Global
{A39B6D7C-6616-40D6-8AE4-C6CEE93D2708} = {CAB438D8-B0F5-4AF0-BEBD-9E2ADBD7B483}
{5E806C9F-1B67-4B6B-A6AB-258834250DBB} = {FFCF518F-2A4A-40A2-9174-2EE13B76C723}
{5FD0133B-69E5-4474-9B67-9FD1D0150C70} = {FFCF518F-2A4A-40A2-9174-2EE13B76C723}
{E8831F32-11D7-D42C-E43C-711998BC357A} = {04E3E11E-B47D-4599-8AFC-50515A95E715}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {53A1F287-EFB2-4D97-A4BB-4A5E145613F6}
Expand Down
6 changes: 5 additions & 1 deletion build-tools/create-packs/Microsoft.Android.Runtime.proj
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,11 @@ projects that use the Microsoft.Android framework in .NET 6+.
<_AndroidRuntimePackAssemblies Include="$(_MonoAndroidNETDefaultOutDir)Java.Interop.dll" />
<_AndroidRuntimePackAssemblies Include="$(_MonoAndroidNETDefaultOutDir)Mono.Android.dll" />
<_AndroidRuntimePackAssemblies Include="$(_MonoAndroidNETDefaultOutDir)Mono.Android.Runtime.dll" />
<!-- Always include stable Mono.Android.Export.dll -->
<!-- Always include stable versions of the following assemblies -->
<_AndroidRuntimePackAssemblies
Include="$(_MonoAndroidNETOutputRoot)$(AndroidLatestStableApiLevel)\Microsoft.Android.Runtime.NativeAOT.dll"
Condition=" '$(AndroidRuntime)' == 'NativeAOT' "
/>
<_AndroidRuntimePackAssemblies Include="$(_MonoAndroidNETOutputRoot)$(AndroidLatestStableApiLevel)\Mono.Android.Export.dll" />
</ItemGroup>

Expand Down
7 changes: 0 additions & 7 deletions samples/NativeAOT/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,13 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application android:allowBackup="true" android:icon="@mipmap/appicon" android:label="@string/app_name" android:roundIcon="@mipmap/appicon_round" android:supportsRtl="true">
<!-- Temporary, to eventually be included in .NET Android infrastructure -->
<provider
android:name="net.dot.jni.nativeaot.NativeAotRuntimeProvider"
android:exported="false"
android:initOrder="1999999999"
android:authorities="net.dot.jni.nativeaot.NativeAotRuntimeProvider.__init__"
/>
</application>
<uses-permission android:name="android.permission.INTERNET" />
</manifest>
8 changes: 0 additions & 8 deletions samples/NativeAOT/NativeAOT.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,6 @@
<ApplicationVersion>1</ApplicationVersion>
<ApplicationDisplayVersion>1.0</ApplicationDisplayVersion>
<AndroidPackageFormat>apk</AndroidPackageFormat>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<!-- Temporary for InternalsVisibleTo -->
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>..\..\product.snk</AssemblyOriginatorKeyFile>
<!-- Default to arm64 device -->
<RuntimeIdentifier>android-arm64</RuntimeIdentifier>
<!-- Current required properties for NativeAOT -->
Expand All @@ -29,8 +25,4 @@
<_FastDeploymentDiagnosticLogging>true</_FastDeploymentDiagnosticLogging>
</PropertyGroup>

<ItemGroup>
<AndroidJavaSource Update="*.java" Bind="false" />
<ProjectReference Include="..\..\external\Java.Interop\src\Java.Runtime.Environment\Java.Runtime.Environment.csproj" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
using Java.Interop;
using System.Runtime.InteropServices;

namespace NativeAOT;
namespace Microsoft.Android.Runtime;

static class JavaInteropRuntime
{
Expand Down Expand Up @@ -35,7 +35,7 @@ static void init (IntPtr jnienv, IntPtr klass)
{
try {
var typeManager = new NativeAotTypeManager ();
var options = new JreRuntimeOptions {
var options = new NativeAotRuntimeOptions {
EnvironmentPointer = jnienv,
TypeManager = typeManager,
ValueManager = new NativeAotValueManager (typeManager),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
using System.Runtime.InteropServices;
using System.Text;

namespace NativeAOT;
namespace Microsoft.Android.Runtime;

internal sealed class LogcatTextWriter : TextWriter {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,35 @@
using Java.Interop;
using Java.Interop.Tools.TypeNameMappings;

namespace NativeAOT;
namespace Microsoft.Android.Runtime;

partial class NativeAotTypeManager : JniRuntime.JniTypeManager {

const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors;
internal const DynamicallyAccessedMemberTypes Methods = DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods;
internal const DynamicallyAccessedMemberTypes MethodsAndPrivateNested = Methods | DynamicallyAccessedMemberTypes.NonPublicNestedTypes;

// TODO: list of types specific to this application
Dictionary<string, Type> typeMappings = new () {
["android/app/Activity"] = typeof (Android.App.Activity),
["android/app/Application"] = typeof (Android.App.Application),
["android/content/Context"] = typeof (Android.Content.Context),
["android/content/ContextWrapper"] = typeof (Android.Content.ContextWrapper),
["android/os/BaseBundle"] = typeof (Android.OS.BaseBundle),
["android/os/Bundle"] = typeof (Android.OS.Bundle),
["android/view/ContextThemeWrapper"] = typeof (Android.Views.ContextThemeWrapper),
["my/MainActivity"] = typeof (MainActivity),
["my/MainApplication"] = typeof (MainApplication),
};
readonly IDictionary<string, Type> TypeMappings = new Dictionary<string, Type> (StringComparer.Ordinal);

public NativeAotTypeManager ()
{
AndroidLog.Print (AndroidLogLevel.Info, "NativeAotTypeManager", $"# jonp: NativeAotTypeManager()");
AndroidLog.Print (AndroidLogLevel.Info, "NativeAotTypeManager", $"NativeAotTypeManager()");
var startTicks = global::System.Environment.TickCount;
InitializeTypeMappings ();
Copy link
Contributor

Choose a reason for hiding this comment

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

This likewise should be in the static constructor.

Alternatively, TypeMappings should be an instance field.

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 made it an instance field, as I think it makes more sense for the instance of the *TypeManager to hold its typemap.

var endTicks = global::System.Environment.TickCount;
AndroidLog.Print (AndroidLogLevel.Info, "NativeAotTypeManager", $"InitializeTypeMappings() took {endTicks - startTicks}ms");
}

protected override Type? GetInvokerTypeCore (Type type)
void InitializeTypeMappings ()
{
// Should be replaced by src/Microsoft.Android.Sdk.ILLink/TypeMappingStep.cs
throw new InvalidOperationException ("TypeMappings should be replaced during trimming!");
}

[return: DynamicallyAccessedMembers (Constructors)]
protected override Type? GetInvokerTypeCore (
[DynamicallyAccessedMembers (Constructors)]
Type type)
{
const string suffix = "Invoker";

Expand Down Expand Up @@ -68,6 +70,10 @@ static Type MakeGenericType (
return MakeGenericType (suffixDefinition, arguments);
}

// NOTE: suppressions below also in `src/Mono.Android/Android.Runtime/AndroidRuntime.cs`
[UnconditionalSuppressMessage ("Trimming", "IL2057", Justification = "Type.GetType() can never statically know the string value parsed from parameter 'methods'.")]
[UnconditionalSuppressMessage ("Trimming", "IL2067", Justification = "Delegate.CreateDelegate() can never statically know the string value parsed from parameter 'methods'.")]
[UnconditionalSuppressMessage ("Trimming", "IL2072", Justification = "Delegate.CreateDelegate() can never statically know the string value parsed from parameter 'methods'.")]
public override void RegisterNativeMembers (
JniType nativeClass,
[DynamicallyAccessedMembers (MethodsAndPrivateNested)]
Expand Down Expand Up @@ -143,7 +149,7 @@ public override void RegisterNativeMembers (
protected override IEnumerable<Type> GetTypesForSimpleReference (string jniSimpleReference)
{
AndroidLog.Print (AndroidLogLevel.Info, "NativeAotTypeManager", $"# jonp: GetTypesForSimpleReference: jniSimpleReference=`{jniSimpleReference}`");
if (typeMappings.TryGetValue (jniSimpleReference, out var target)) {
if (TypeMappings.TryGetValue (jniSimpleReference, out var target)) {
Console.WriteLine ($"# jonp: GetTypesForSimpleReference: jniSimpleReference=`{jniSimpleReference}` -> `{target}`");
yield return target;
}
Expand All @@ -161,9 +167,7 @@ protected override IEnumerable<string> GetSimpleReferences (Type type)

IEnumerable<string> CreateSimpleReferencesEnumerator (Type type)
{
if (typeMappings == null)
yield break;
foreach (var e in typeMappings) {
foreach (var e in TypeMappings) {
if (e.Value == type)
yield return e.Key;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
Expand All @@ -13,9 +14,9 @@
using Android.Runtime;
using Java.Interop;

namespace NativeAOT;
namespace Microsoft.Android.Runtime;

internal class NativeAotValueManager : JniRuntime.JniValueManager
class NativeAotValueManager : JniRuntime.JniValueManager
{
const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors;

Expand Down Expand Up @@ -113,12 +114,12 @@ void WarnNotReplacing (int key, IJavaPeerable ignoreValue, IJavaPeerable keepVal
"Warning: Not registering PeerReference={0} IdentityHashCode=0x{1} Instance={2} Instance.Type={3} Java.Type={4}; " +
"keeping previously registered PeerReference={5} Instance={6} Instance.Type={7} Java.Type={8}.",
ignoreValue.PeerReference.ToString (),
key.ToString ("x"),
RuntimeHelpers.GetHashCode (ignoreValue).ToString ("x"),
key.ToString ("x", CultureInfo.InvariantCulture),
RuntimeHelpers.GetHashCode (ignoreValue).ToString ("x", CultureInfo.InvariantCulture),
ignoreValue.GetType ().FullName,
JniEnvironment.Types.GetJniTypeNameFromInstance (ignoreValue.PeerReference),
keepValue.PeerReference.ToString (),
RuntimeHelpers.GetHashCode (keepValue).ToString ("x"),
RuntimeHelpers.GetHashCode (keepValue).ToString ("x", CultureInfo.InvariantCulture),
keepValue.GetType ().FullName,
JniEnvironment.Types.GetJniTypeNameFromInstance (keepValue.PeerReference));
}
Expand Down Expand Up @@ -186,8 +187,8 @@ public override void FinalizePeer (IJavaPeerable value)
if (o.LogGlobalReferenceMessages) {
o.WriteGlobalReferenceLine ("Finalizing PeerReference={0} IdentityHashCode=0x{1} Instance=0x{2} Instance.Type={3}",
h.ToString (),
value.JniIdentityHashCode.ToString ("x"),
RuntimeHelpers.GetHashCode (value).ToString ("x"),
value.JniIdentityHashCode.ToString ("x", CultureInfo.InvariantCulture),
RuntimeHelpers.GetHashCode (value).ToString ("x", CultureInfo.InvariantCulture),
value.GetType ().ToString ());
}
RemovePeer (value);
Expand All @@ -200,8 +201,8 @@ public override void FinalizePeer (IJavaPeerable value)
if (o.LogGlobalReferenceMessages) {
o.WriteGlobalReferenceLine ("Finalizing PeerReference={0} IdentityHashCode=0x{1} Instance=0x{2} Instance.Type={3}",
h.ToString (),
value.JniIdentityHashCode.ToString ("x"),
RuntimeHelpers.GetHashCode (value).ToString ("x"),
value.JniIdentityHashCode.ToString ("x", CultureInfo.InvariantCulture),
RuntimeHelpers.GetHashCode (value).ToString ("x", CultureInfo.InvariantCulture),
value.GetType ().ToString ());
}
value.SetPeerReference (new JniObjectReference ());
Expand All @@ -214,9 +215,11 @@ public override void ActivatePeer (IJavaPeerable? self, JniObjectReference refer
try {
ActivateViaReflection (reference, cinfo, argumentValues);
} catch (Exception e) {
var m = string.Format ("Could not activate {{ PeerReference={0} IdentityHashCode=0x{1} Java.Type={2} }} for managed type '{3}'.",
var m = string.Format (
CultureInfo.InvariantCulture,
"Could not activate {{ PeerReference={0} IdentityHashCode=0x{1} Java.Type={2} }} for managed type '{3}'.",
reference,
GetJniIdentityHashCode (reference).ToString ("x"),
GetJniIdentityHashCode (reference).ToString ("x", CultureInfo.InvariantCulture),
JniEnvironment.Types.GetJniTypeNameFromInstance (reference),
cinfo.DeclaringType?.FullName);
Debug.WriteLine (m);
Expand Down Expand Up @@ -257,7 +260,11 @@ public override List<JniSurfacedPeerInfo> GetSurfacedPeers ()

static readonly Type[] XAConstructorSignature = new Type [] { typeof (IntPtr), typeof (JniHandleOwnership) };

protected override IJavaPeerable? TryCreatePeer (ref JniObjectReference reference, JniObjectReferenceOptions options, Type type)
protected override IJavaPeerable? TryCreatePeer (
ref JniObjectReference reference,
JniObjectReferenceOptions options,
[DynamicallyAccessedMembers (Constructors)]
Type type)
{
var c = type.GetConstructor (ActivationConstructorBindingFlags, null, XAConstructorSignature, null);
if (c != null) {
Expand Down
5 changes: 5 additions & 0 deletions src/Microsoft.Android.Runtime.NativeAOT/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
using System.Runtime.Versioning;

// NOTE: silences the CA1416 analyzer about supported Android APIs
[assembly: TargetPlatformAttribute("Android35.0")]
[assembly: SupportedOSPlatformAttribute("Android21.0")]
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// Originally from: https://github.com/dotnet/java-interop/blob/dd3c1d0514addfe379f050627b3e97493e985da6/src/Java.Runtime.Environment/Java.Interop/JreRuntime.cs
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading;
using Microsoft.Android.Runtime;

namespace Java.Interop {

struct JavaVMInitArgs {
#pragma warning disable CS0649 // Field is never assigned to;
public JniVersion version; /* use JNI_VERSION_1_2 or later */

public int nOptions;
public IntPtr /* JavaVMOption[] */ options;
public byte ignoreUnrecognized;
#pragma warning restore CS0649
}

class NativeAotRuntimeOptions : JniRuntime.CreationOptions {

public bool IgnoreUnrecognizedOptions {get; set;}

public TextWriter? JniGlobalReferenceLogWriter {get; set;}
public TextWriter? JniLocalReferenceLogWriter {get; set;}

public NativeAotRuntimeOptions ()
{
JniVersion = JniVersion.v1_2;
}

public JreRuntime CreateJreVM ()
{
return new JreRuntime (this);
}
}

class JreRuntime : JniRuntime
{
static JreRuntime ()
{
}

static NativeAotRuntimeOptions CreateJreVM (NativeAotRuntimeOptions builder)
{
if (builder == null)
throw new ArgumentNullException ("builder");
if (builder.InvocationPointer == IntPtr.Zero &&
builder.EnvironmentPointer == IntPtr.Zero &&
string.IsNullOrEmpty (builder.JvmLibraryPath))
throw new InvalidOperationException ($"Member `{nameof (NativeAotRuntimeOptions)}.{nameof (NativeAotRuntimeOptions.JvmLibraryPath)}` must be set.");

#if NET
builder.TypeManager ??= new NativeAotTypeManager ();
#endif // NET

builder.ValueManager ??= new NativeAotValueManager (builder.TypeManager);
builder.ObjectReferenceManager ??= new ManagedObjectReferenceManager (builder.JniGlobalReferenceLogWriter, builder.JniLocalReferenceLogWriter);

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

throw new NotImplementedException ();
}

[UnconditionalSuppressMessage ("Trimming", "IL3000", Justification = "We check for a null Assembly.Location value!")]
internal static string? GetAssemblyLocation (Assembly assembly)
{
var location = assembly.Location;
if (!string.IsNullOrEmpty (location))
return location;
return null;
}

internal protected JreRuntime (NativeAotRuntimeOptions builder)
: base (CreateJreVM (builder))
{
}

public override string? GetCurrentManagedThreadName ()
{
return Thread.CurrentThread.Name;
}

public override string GetCurrentManagedThreadStackTrace (int skipFrames, bool fNeedFileInfo)
{
return new StackTrace (skipFrames, fNeedFileInfo)
.ToString ();
}
}
}
Loading
Loading