-
Notifications
You must be signed in to change notification settings - Fork 538
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[NativeAOT] add sample that runs successfully (#9636)
Context: https://github.com/dotnet/java-interop/tree/9b1d8781e8e322849d05efac32119c913b21c192/samples/Hello-NativeAOTFromAndroid "Import" the [Hello-NativeAOTFromAndroid][0] from dotnet/java-interop, and update it to use `Mono.Android.dll` and parts of the .NET for Android build system. It currently relies on `[InternalsVisibleTo("NativeAOT")]` within `Mono.Android.dll` for access to things like: * `Android.Runtime.JNIEnvInit.InitializeJniRuntime()` There are a couple MSBuild changes still left: TODO: * `$(_ExtraTrimmerArgs)` needs to be specified for trimmer warnings to not be displayed twice. This is the same thing done in xamarin/xamarin-macios. * `@(TrimmerRootAssembly)` needs to be set for illink's "already trimmed" output for ILC. We exclude `System.Private.CoreLib.dll` from this list. * Remove use of `Java.Runtime.Environment.dll`, and otherwise allow the sample to be built from the .NET for Android workload packs. * "MOAR Integration": sample depends upon a manually specified "type map" dictionary. This needs to be automagic to be useful. * Figure out what to do about C++: do we dynamically link against and bundle `libc++_shared.so`? Statically link against `libc++_static.a`? [0]: dotnet/java-interop@78d5937
- Loading branch information
1 parent
80bb5d7
commit 7a772f0
Showing
34 changed files
with
521 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
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 (), | ||
ValueManager = new NativeAotValueManager (), | ||
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.InitializeJniRuntime (runtime); | ||
} | ||
catch (Exception e) { | ||
AndroidLog.Print (AndroidLogLevel.Error, "JavaInteropRuntime", $"JavaInteropRuntime.init: error: {e}"); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
<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> | ||
<!-- Default to arm64 device --> | ||
<RuntimeIdentifier>android-arm64</RuntimeIdentifier> | ||
<!-- Current required properties for NativeAOT --> | ||
<PublishAot>true</PublishAot> | ||
<PublishAotUsingRuntimePack>true</PublishAotUsingRuntimePack> | ||
</PropertyGroup> | ||
|
||
<!-- Settings for CI --> | ||
<PropertyGroup Condition=" '$(RunningOnCI)' == 'true' "> | ||
<!-- x86_64 emulator --> | ||
<RuntimeIdentifier>android-x64</RuntimeIdentifier> | ||
<_NuGetFolderOnCI>..\..\bin\Build$(Configuration)\nuget-unsigned</_NuGetFolderOnCI> | ||
<RestoreAdditionalProjectSources Condition="Exists('$(_NuGetFolderOnCI)')">$(_NuGetFolderOnCI)</RestoreAdditionalProjectSources> | ||
<_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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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."); | ||
} | ||
} |
Oops, something went wrong.