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

[NativeAOT] add sample that runs successfully #9636

Merged
merged 21 commits into from
Jan 22, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
984cd34
[NativeAOT] add sample that runs successfully
jonathanpeppers Dec 19, 2024
3798f71
Update NativeAotTypeManager.cs
jonathanpeppers Dec 19, 2024
a906d79
Update NativeAotTypeManager.cs
jonathanpeppers Dec 19, 2024
8459aa5
Update NativeAotTypeManager.cs
jonathanpeppers Dec 19, 2024
4fd9dd1
Add test that runs NativeAOT on emulator
jonathanpeppers Dec 19, 2024
263e94c
Fix missing test attachments
jonathanpeppers Dec 19, 2024
8cde307
$(RestoreAdditionalProjectSources)
jonathanpeppers Dec 19, 2024
01c95db
$(RestoreAdditionalProjectSources) take 2
jonathanpeppers Dec 20, 2024
d347942
There is now only 1 *ValueManager
jonathanpeppers Dec 20, 2024
e7caafc
Run NativeAOTSample Release mode
jonathanpeppers Dec 20, 2024
47bb011
Update JNIEnvInit.cs
jonathanpeppers Dec 20, 2024
b559b8d
Prepare java.interop before emulator tests
jonathanpeppers Dec 20, 2024
c0f6676
Merge branch 'main' into dev/peppers/nativeaot/runs2
jonathanpeppers Jan 17, 2025
f1e79a1
Updates for changes in main
jonathanpeppers Jan 17, 2025
6610a8a
Rename Initialize -> InitializeJniRuntime
jonathanpeppers Jan 17, 2025
78b275c
Revert "Prepare java.interop before emulator tests"
jonathanpeppers Jan 21, 2025
a3db35d
PrepareJavaInterop in different yaml file
jonathanpeppers Jan 21, 2025
3dd6c7a
$(RunningOnCI) settings
jonathanpeppers Jan 21, 2025
05d94ef
Use `-r android-x64` on CI
jonathanpeppers Jan 21, 2025
be81ab3
Fix x86_64 toolchain
jonathanpeppers Jan 21, 2025
8fe6bfe
Update Microsoft.Android.Sdk.NativeAOT.targets
jonathanpeppers Jan 21, 2025
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
13 changes: 13 additions & 0 deletions samples/NativeAOT/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?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>
53 changes: 53 additions & 0 deletions samples/NativeAOT/JavaInteropRuntime.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
using Android.Runtime;
using Java.Interop;
using System.Runtime.InteropServices;

namespace NativeAOT;

static class JavaInteropRuntime
{
static JniRuntime? runtime;

[UnmanagedCallersOnly (EntryPoint="JNI_OnLoad")]
static int JNI_OnLoad (IntPtr vm, IntPtr reserved)
{
try {
AndroidLog.Print (AndroidLogLevel.Info, "JavaInteropRuntime", "JNI_OnLoad()");
LogcatTextWriter.Init ();
return (int) JniVersion.v1_6;
}
catch (Exception e) {
AndroidLog.Print (AndroidLogLevel.Error, "JavaInteropRuntime", $"JNI_OnLoad() failed: {e}");
return 0;
}
}

[UnmanagedCallersOnly (EntryPoint="JNI_OnUnload")]
static void JNI_OnUnload (IntPtr vm, IntPtr reserved)
{
AndroidLog.Print(AndroidLogLevel.Info, "JavaInteropRuntime", "JNI_OnUnload");
runtime?.Dispose ();
}

// symbol name from `$(IntermediateOutputPath)obj/Release/osx-arm64/h-classes/net_dot_jni_hello_JavaInteropRuntime.h`
[UnmanagedCallersOnly (EntryPoint="Java_net_dot_jni_nativeaot_JavaInteropRuntime_init")]
static void init (IntPtr jnienv, IntPtr klass)
{
try {
var options = new JreRuntimeOptions {
EnvironmentPointer = jnienv,
TypeManager = new NativeAotTypeManager (),
UseMarshalMemberBuilder = false,
JniGlobalReferenceLogWriter = new LogcatTextWriter (AndroidLogLevel.Debug, "NativeAot:GREF"),
JniLocalReferenceLogWriter = new LogcatTextWriter (AndroidLogLevel.Debug, "NativeAot:LREF"),
};
runtime = options.CreateJreVM ();

// Entry point into Mono.Android.dll
JNIEnvInit.InitializeNativeAot (runtime);
}
catch (Exception e) {
AndroidLog.Print (AndroidLogLevel.Error, "JavaInteropRuntime", $"JavaInteropRuntime.init: error: {e}");
}
}
}
15 changes: 15 additions & 0 deletions samples/NativeAOT/JavaInteropRuntime.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package net.dot.jni.nativeaot;

import android.util.Log;

public class JavaInteropRuntime {
static {
Log.d("JavaInteropRuntime", "Loading NativeAOT.so...");
System.loadLibrary("NativeAOT");
}

private JavaInteropRuntime() {
}

public static native void init();
}
77 changes: 77 additions & 0 deletions samples/NativeAOT/Logging.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// NOTE: logging methods below are need temporarily due to:
// 1) linux-bionic BCL doesn't redirect stdout/stderr to logcat
// 2) Android.Util.Log won't work until we initialize the Java.Interop.JreRuntime

using System.IO;
using System.Runtime.InteropServices;
using System.Text;

namespace NativeAOT;

internal sealed class LogcatTextWriter : TextWriter {

public static void Init ()
{
// This method is a no-op, but it's necessary to ensure the static
// constructor is executed.
}

static LogcatTextWriter ()
{
Console.SetOut (new LogcatTextWriter (AndroidLogLevel.Info));
Console.SetError (new LogcatTextWriter (AndroidLogLevel.Error));
}

AndroidLogLevel Level;
string Tag;

internal LogcatTextWriter (AndroidLogLevel level, string tag = "NativeAotFromAndroid")
{
Level = level;
Tag = tag;
}

public override Encoding Encoding => Encoding.UTF8;
public override string NewLine => "\n";

public override void WriteLine (string? value)
{
if (value == null) {
AndroidLog.Print (Level, Tag, "");
return;
}
ReadOnlySpan<char> span = value;
while (!span.IsEmpty) {
if (span.IndexOf ('\n') is int n && n < 0) {
break;
}
var line = span.Slice (0, n);
AndroidLog.Print (Level, Tag, line.ToString ());
span = span.Slice (n + 1);
}
AndroidLog.Print (Level, Tag, span.ToString ());
}
}

static class AndroidLog {

[DllImport ("log", EntryPoint = "__android_log_print", CallingConvention = CallingConvention.Cdecl)]
private static extern void __android_log_print(AndroidLogLevel level, string? tag, string format, string args, IntPtr ptr);

internal static void Print(AndroidLogLevel level, string? tag, string message) =>
__android_log_print(level, tag, "%s", message, IntPtr.Zero);

}

internal enum AndroidLogLevel
{
Unknown = 0x00,
Default = 0x01,
Verbose = 0x02,
Debug = 0x03,
Info = 0x04,
Warn = 0x05,
Error = 0x06,
Fatal = 0x07,
Silent = 0x08
}
18 changes: 18 additions & 0 deletions samples/NativeAOT/MainActivity.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using Android.Runtime;
using System.Reflection;
using System.Runtime.InteropServices;

namespace NativeAOT;

[Register("my/MainActivity")] // Required for typemap in NativeAotTypeManager
[Activity(Label = "@string/app_name", MainLauncher = true)]
public class MainActivity : Activity
{
protected override void OnCreate(Bundle? savedInstanceState)
{
base.OnCreate(savedInstanceState);

// Set our view from the "main" layout resource
SetContentView(Resource.Layout.activity_main);
}
}
25 changes: 25 additions & 0 deletions samples/NativeAOT/NativeAOT.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>$(DotNetAndroidTargetFramework)</TargetFramework>
<SupportedOSPlatformVersion>21</SupportedOSPlatformVersion>
<OutputType>Exe</OutputType>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<ApplicationId>net.dot.hellonativeaot</ApplicationId>
<ApplicationVersion>1</ApplicationVersion>
<ApplicationDisplayVersion>1.0</ApplicationDisplayVersion>
<AndroidPackageFormat>apk</AndroidPackageFormat>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<!-- Temporary for InternalsVisibleTo -->
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>..\..\product.snk</AssemblyOriginatorKeyFile>
<!-- Current required properties for NativeAOT -->
<RuntimeIdentifier>android-arm64</RuntimeIdentifier>
<PublishAot>true</PublishAot>
<PublishAotUsingRuntimePack>true</PublishAotUsingRuntimePack>
</PropertyGroup>
<ItemGroup>
<AndroidJavaSource Update="*.java" Bind="false" />
<ProjectReference Include="..\..\external\Java.Interop\src\Java.Runtime.Environment\Java.Runtime.Environment.csproj" />
</ItemGroup>
</Project>
51 changes: 51 additions & 0 deletions samples/NativeAOT/NativeAotRuntimeProvider.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package net.dot.jni.nativeaot;

import android.util.Log;

public class NativeAotRuntimeProvider
extends android.content.ContentProvider
{
private static final String TAG = "NativeAotRuntimeProvider";

public NativeAotRuntimeProvider() {
Log.d(TAG, "NativeAotRuntimeProvider()");
}

@Override
public boolean onCreate() {
Log.d(TAG, "NativeAotRuntimeProvider.onCreate()");
return true;
}

@Override
public void attachInfo(android.content.Context context, android.content.pm.ProviderInfo info) {
Log.d(TAG, "NativeAotRuntimeProvider.attachInfo(): calling JavaInteropRuntime.init()…");
JavaInteropRuntime.init();
super.attachInfo (context, info);
}

@Override
public android.database.Cursor query(android.net.Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
throw new RuntimeException ("This operation is not supported.");
}

@Override
public String getType(android.net.Uri uri) {
throw new RuntimeException ("This operation is not supported.");
}

@Override
public android.net.Uri insert(android.net.Uri uri, android.content.ContentValues initialValues) {
throw new RuntimeException ("This operation is not supported.");
}

@Override
public int delete(android.net.Uri uri, String where, String[] whereArgs) {
throw new RuntimeException ("This operation is not supported.");
}

@Override
public int update(android.net.Uri uri, android.content.ContentValues values, String where, String[] whereArgs) {
throw new RuntimeException ("This operation is not supported.");
}
}
Loading
Loading