Skip to content

Commit

Permalink
[Mono.Android] handle exceptions in RegisterNativeMembers
Browse files Browse the repository at this point in the history
Context: dotnet/maui#4262

`dotnet new maui-blazor` crashes at runtime, if you do:

    dotnet build -t:Run -c Release

You can turn *off* the linker to solve this issue:

    dotnet build -t:Run -c Release -p:AndroidLinkMode=None

(or `PublishTrimmed=false`)

The app crashes in way that you get a native crash:

    backtrace:
    #00 pc 000000000065d8fc  /apex/com.android.art/lib64/libart.so (void art::StackVisitor::WalkStack<(art::StackVisitor::CountTransitions)0>(bool)+156) (BuildId: 7fbaf2a1a3317bd634b00eb90e32291e)
    #1 pc 000000000069b25d  /apex/com.android.art/lib64/libart.so (art::Thread::GetCurrentMethod(unsigned int*, bool, bool) const+157) (BuildId: 7fbaf2a1a3317bd634b00eb90e32291e)
    #2 pc 0000000000430fed  /apex/com.android.art/lib64/libart.so (art::JNI<false>::FindClass(_JNIEnv*, char const*)+765) (BuildId: 7fbaf2a1a3317bd634b00eb90e32291e)
    #3 pc 0000000000047e5a  /data/app/~~0Qm6D1S0sO3f1lwfakN0PA==/com.companyname.mauiapp2-08UokVCH5k_PlbZEH_hhkA==/split_config.x86_64.apk!libmono-android.release.so (offset 0x11e000) (java_interop_jnienv_find_class+26) (BuildId: 3d04f8b946590175e97b89aee2e3b19ceed4b524)
    #4 pc 00000000000128ac  <anonymous:41640000>

After much investigation... We found that
`Java.IO.InputStreamHandler.GetReadHandler()` was linked away.

This is because:

1. `Mono.Android.dll` has it's Java stubs precompiled into
   `mono.android.jar` and `mono.android.dex`.

2. Since `Mono.Android.dll` doesn't go through `GenerateJavaStubs`,
   the linker has no way to know if the missing method is used -- it
   is only called from Java in the crashing app.

To make the crash better, we need to `try-catch` *all* exceptions in
`RegisterNativeMembers()` and pass them to
`AndroidRuntime.RaisePendingException()`. This is because
`RegisterNativeMembers()` is called directly from Java.

Now the crash shows:

    01-26 12:20:20.290 29264 29264 I MonoDroid: Android.Runtime.JavaProxyThrowable: Exception_WasThrown, Android.Runtime.JavaProxyThrowable
    01-26 12:20:20.290 29264 29264 I MonoDroid:
    01-26 12:20:20.290 29264 29264 I MonoDroid:   --- End of managed Android.Runtime.JavaProxyThrowable stack trace ---
    01-26 12:20:20.290 29264 29264 I MonoDroid: android.runtime.JavaProxyThrowable: System.ArgumentException: Arg_DlgtTargMeth
    01-26 12:20:20.290 29264 29264 I MonoDroid:    at System.Delegate.CreateDelegate(Type , Type , String , Boolean , Boolean )
    01-26 12:20:20.290 29264 29264 I MonoDroid:    at System.Delegate.CreateDelegate(Type , Type , String )
    01-26 12:20:20.290 29264 29264 I MonoDroid:    at Android.Runtime.AndroidTypeManager.RegisterNativeMembers(JniType , Type , String )
    01-26 12:20:20.290 29264 29264 I MonoDroid: --- End of stack trace from previous location ---
    01-26 12:20:20.290 29264 29264 I MonoDroid:    at Java.Interop.JniEnvironment.StaticMethods.CallStaticObjectMethod(JniObjectReference , JniMethodInfo , JniArgumentValue* )
    01-26 12:20:20.290 29264 29264 I MonoDroid:    at Android.Runtime.JNIEnv.CallStaticObjectMethod(IntPtr , IntPtr , JValue* )
    01-26 12:20:20.290 29264 29264 I MonoDroid:    at Android.Runtime.JNIEnv.CallStaticObjectMethod(IntPtr , IntPtr , JValue[] )
    01-26 12:20:20.290 29264 29264 I MonoDroid:    at Android.Runtime.JNIEnv.FindClass(String )
    01-26 12:20:20.290 29264 29264 I MonoDroid:    at Android.Runtime.JNIEnv.AllocObject(String )
    01-26 12:20:20.290 29264 29264 I MonoDroid:    at Android.Runtime.JNIEnv.StartCreateInstance(String , String , JValue* )
    01-26 12:20:20.290 29264 29264 I MonoDroid:    at Android.Runtime.JNIEnv.StartCreateInstance(String , String , JValue[] )
    01-26 12:20:20.290 29264 29264 I MonoDroid:    at Android.Runtime.InputStreamAdapter..ctor(Stream )
    01-26 12:20:20.290 29264 29264 I MonoDroid:    at Android.Runtime.InputStreamAdapter.ToLocalJniHandle(Stream )
    01-26 12:20:20.290 29264 29264 I MonoDroid:    at Android.Webkit.WebResourceResponse..ctor(String , String , Int32 , String , IDictionary`2 , Stream )
    01-26 12:20:20.290 29264 29264 I MonoDroid:    at Microsoft.AspNetCore.Components.WebView.Maui.WebKitWebViewClient.ShouldInterceptRequest(WebView view, IWebResourceRequest request)
    01-26 12:20:20.290 29264 29264 I MonoDroid:    at Android.Webkit.WebViewClient.n_ShouldInterceptRequest_Landroid_webkit_WebView_Landroid_webkit_WebResourceRequest_(IntPtr , IntPtr , IntPtr , IntPtr )
    01-26 12:20:20.290 29264 29264 I MonoDroid: 	at crc64d693e2d9159537db.WebKitWebViewClient.n_shouldInterceptRequest(Native Method)
    01-26 12:20:20.290 29264 29264 I MonoDroid: 	at crc64d693e2d9159537db.WebKitWebViewClient.shouldInterceptRequest(WebKitWebViewClient.java:39)
    01-26 12:20:20.290 29264 29264 I MonoDroid: 	at Rr.a(chromium-TrichromeWebViewGoogle.apk-stable-410410686:16)
    01-26 12:20:20.290 29264 29264 I MonoDroid: 	at org.chromium.android_webview.AwContentsBackgroundThreadClient.shouldInterceptRequestFromNative(chromium-TrichromeWebViewGoogle.apk-stable-410410686:2)
    01-26 12:20:20.290 29264 29264 I MonoDroid:
    01-26 12:20:20.290 29264 29264 I MonoDroid:   --- End of managed Android.Runtime.JavaProxyThrowable stack trace ---
    01-26 12:20:20.290 29264 29264 I MonoDroid: android.runtime.JavaProxyThrowable: System.ArgumentException: Arg_DlgtTargMeth
    01-26 12:20:20.290 29264 29264 I MonoDroid:    at System.Delegate.CreateDelegate(Type , Type , String , Boolean , Boolean )
    01-26 12:20:20.290 29264 29264 I MonoDroid:    at System.Delegate.CreateDelegate(Type , Type , String )
    01-26 12:20:20.290 29264 29264 I MonoDroid:    at Android.Runtime.AndroidTypeManager.RegisterNativeMembers(JniType , Type , String )
    01-26 12:20:20.290 29264 29264 I MonoDroid: --- End of stack trace from previous location ---
    01-26 12:20:20.290 29264 29264 I MonoDroid:    at Java.Interop.JniEnvironment.StaticMethods.CallStaticObjectMethod(JniObjectReference , JniMethodInfo , JniArgumentValue* )
    01-26 12:20:20.290 29264 29264 I MonoDroid:    at Android.Runtime.JNIEnv.CallStaticObjectMethod(IntPtr , IntPtr , JValue* )
    01-26 12:20:20.290 29264 29264 I MonoDroid:    at Android.Runtime.JNIEnv.CallStaticObjectMethod(IntPtr , IntPtr , JValue[] )
    01-26 12:20:20.290 29264 29264 I MonoDroid:    at Android.Runtime.JNIEnv.FindClass(String )
    01-26 12:20:20.290 29264 29264 I MonoDroid:    at Android.Runtime.JNIEnv.AllocObject(String )
    01-26 12:20:20.290 29264 29264 I MonoDroid:    at Android.Runtime.JNIEnv.StartCreateInstance(String , String , JValue* )
    01-26 12:20:20.290 29264 29264 I MonoDroid:    at Android.Runtime.JNIEnv.StartCreateInstance(String , String , JValue[] )
    01-26 12:20:20.290 29264 29264 I MonoDroid:    at Android.Runtime.InputStreamAdapter..ctor(Stream )
    01-26 12:20:20.290 29264 29264 I MonoDroid:    at Android.Runtime.InputStreamAdapter.ToLocalJniHandle(Stream )
    01-26 12:20:20.290 29264 29264 I MonoDroid:    at Android.Webkit.WebResourceResponse..ctor(String , String , Int32 , String , IDictionary`2 , Stream )
    01-26 12:20:20.290 29264 29264 I MonoDroid:    at Microsoft.AspNetCore.Components.WebView.Maui.WebKitWebViewClient.ShouldInterceptRequest(WebView view, IWebResourceRequest request)
    01-26 12:20:20.290 29264 29264 I MonoDroid:    at Android.Webkit.WebViewClient.n_ShouldInterceptRequest_Landroid_webkit_WebView_Landroid_webkit_WebResourceRequest_(IntPtr , IntPtr , IntPtr , IntPtr )
    01-26 12:20:20.290 29264 29264 I MonoDroid: 	at crc64d693e2d9159537db.WebKitWebViewClient.n_shouldInterceptRequest(Native Method)
    01-26 12:20:20.290 29264 29264 I MonoDroid: 	at crc64d693e2d9159537db.WebKitWebViewClient.shouldInterceptRequest(WebKitWebViewClient.java:39)
    01-26 12:20:20.290 29264 29264 I MonoDroid: 	at Rr.a(chromium-TrichromeWebViewGoogle.apk-stable-410410686:16)
    01-26 12:20:20.290 29264 29264 I MonoDroid: 	at org.chromium.android_webview.AwContentsBackgroundThreadClient.shouldInterceptRequestFromNative(chromium-TrichromeWebViewGoogle.apk-stable-410410686:2)

This is much easier to reason about, and will save us time in the future.

A fix to the actual linker problem will be coming in another PR.
  • Loading branch information
jonathanpeppers committed Jan 26, 2022
1 parent df84d27 commit 1e7fb27
Showing 1 changed file with 48 additions and 44 deletions.
92 changes: 48 additions & 44 deletions src/Mono.Android/Android.Runtime/AndroidRuntime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -378,57 +378,61 @@ static bool CallRegisterMethodByIndex (JniNativeMethodRegistrationArguments argu

public override void RegisterNativeMembers (JniType nativeClass, Type type, string? methods)
{
if (FastRegisterNativeMembers (nativeClass, type, methods))
return;
try {
if (FastRegisterNativeMembers (nativeClass, type, methods))
return;

if (string.IsNullOrEmpty (methods)) {
if (jniAddNativeMethodRegistrationAttributePresent)
base.RegisterNativeMembers (nativeClass, type, methods);
return;
}
if (string.IsNullOrEmpty (methods)) {
if (jniAddNativeMethodRegistrationAttributePresent)
base.RegisterNativeMembers (nativeClass, type, methods);
return;
}

string[] members = methods!.Split ('\n');
if (members.Length < 2) {
if (jniAddNativeMethodRegistrationAttributePresent)
base.RegisterNativeMembers (nativeClass, type, methods);
return;
}
string[] members = methods!.Split ('\n');
if (members.Length < 2) {
if (jniAddNativeMethodRegistrationAttributePresent)
base.RegisterNativeMembers (nativeClass, type, methods);
return;
}

JniNativeMethodRegistration[] natives = new JniNativeMethodRegistration [members.Length-1];
for (int i = 0; i < members.Length; ++i) {
string method = members [i];
if (string.IsNullOrEmpty (method))
continue;
string[] toks = members [i].Split (new[]{':'}, 4);
Delegate callback;
if (toks [2] == "__export__") {
var mname = toks [0].Substring (2);
MethodInfo? minfo = null;
foreach (var mi in type.GetMethods (BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static))
if (mi.Name == mname && JavaNativeTypeManager.GetJniSignature (mi) == toks [1]) {
minfo = mi;
break;
JniNativeMethodRegistration[] natives = new JniNativeMethodRegistration [members.Length-1];
for (int i = 0; i < members.Length; ++i) {
string method = members [i];
if (string.IsNullOrEmpty (method))
continue;
string[] toks = members [i].Split (new[]{':'}, 4);
Delegate callback;
if (toks [2] == "__export__") {
var mname = toks [0].Substring (2);
MethodInfo? minfo = null;
foreach (var mi in type.GetMethods (BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static))
if (mi.Name == mname && JavaNativeTypeManager.GetJniSignature (mi) == toks [1]) {
minfo = mi;
break;
}

if (minfo == null)
throw new InvalidOperationException (String.Format ("Specified managed method '{0}' was not found. Signature: {1}", mname, toks [1]));
callback = CreateDynamicCallback (minfo);
} else {
Type callbackDeclaringType = type;
if (toks.Length == 4) {
callbackDeclaringType = Type.GetType (toks [3], throwOnError: true)!;
}

if (minfo == null)
throw new InvalidOperationException (String.Format ("Specified managed method '{0}' was not found. Signature: {1}", mname, toks [1]));
callback = CreateDynamicCallback (minfo);
} else {
Type callbackDeclaringType = type;
if (toks.Length == 4) {
callbackDeclaringType = Type.GetType (toks [3], throwOnError: true)!;
}
while (callbackDeclaringType.ContainsGenericParameters) {
callbackDeclaringType = callbackDeclaringType.BaseType!;
while (callbackDeclaringType.ContainsGenericParameters) {
callbackDeclaringType = callbackDeclaringType.BaseType!;
}
GetCallbackHandler connector = (GetCallbackHandler) Delegate.CreateDelegate (typeof (GetCallbackHandler),
callbackDeclaringType, toks [2]);
callback = connector ();
}
GetCallbackHandler connector = (GetCallbackHandler) Delegate.CreateDelegate (typeof (GetCallbackHandler),
callbackDeclaringType, toks [2]);
callback = connector ();
natives [i] = new JniNativeMethodRegistration (toks [0], toks [1], callback);
}
natives [i] = new JniNativeMethodRegistration (toks [0], toks [1], callback);
}

JniEnvironment.Types.RegisterNatives (nativeClass.PeerReference, natives, natives.Length);
JniEnvironment.Types.RegisterNatives (nativeClass.PeerReference, natives, natives.Length);
} catch (Exception e) {
JniEnvironment.Runtime.RaisePendingException (e);
}
}
}

Expand Down

0 comments on commit 1e7fb27

Please sign in to comment.