Skip to content

Using the Export attribute with a constructor with parameter generates a wrong ACW #7554

@hig-dev

Description

@hig-dev

Android application type

Android for .NET (net6.0-android, etc.)

Affected platform version

net6.0-android, net7.0-android

Description

I need a class which looks like this:

[Register("com.bug.exportconstructor.SampleManagedClass")]
public class SampleManagedClass : Java.Lang.Object
{
    [Export(SuperArgumentsString = "")]
    public SampleManagedClass(Context context)
    {
        System.Diagnostics.Debug.WriteLine("success");
    }

    public SampleManagedClass(nint handle, JniHandleOwnership transfer)
    {
    }

    public SampleManagedClass()
    {
    }
}

So the class inherits from Java.Lang.Object and has an exported constructor with the parameter Android.Content.Context.
The resulting code for the constructor in the ACW (Android Callable Wrapper) looks like this:

public SampleManagedClass (android.content.Context p0)
{
    super ();
    if (getClass () == SampleManagedClass.class) {
	mono.android.TypeManager.Activate ("ExportConstructorBug.SampleManagedClass, ExportConstructorBug", "", this, new java.lang.Object[] { p0 });
    }
}

But should look like this:

public SampleManagedClass (android.content.Context p0)
{
    super ();
    if (getClass () == SampleManagedClass.class) {
	mono.android.TypeManager.Activate ("ExportConstructorBug.SampleManagedClass, ExportConstructorBug", "Android.Content.Context, Mono.Android", this, new java.lang.Object[] { p0 });
    }
}

So the generator of the ACW forgets to add the appropriate type information in the TypeManager.Activate call.

The result of this problem is the following exception when the affected class is instantiated with the affected constructor in an unmanaged way:

**System.NotSupportedException:** 'Could not activate JNI Handle 0x7ffd2bd94e70 (key_handle 0x65d856e) of Java type 'com/bug/exportconstructor/SampleManagedClass' as managed type 'ExportConstructorBug.SampleManagedClass'.'

Background information:
I encountered this problem when I wanted to use the Samsung Accessory SDK in my net6.0-android app.
This SDK expects a constructor of this type and calls it from an external Android service.
So this issue is slightly related to #3490.

Steps to Reproduce

  1. Download the minimal reproducible sample app from: ExportConstructorBug.zip

  2. All relevant code is in MainActivity.cs

  3. Run the app -> line 23 will throw System.NotSupportedException

This is the full code of MainActivity.cs:

using Android.Content;
using Android.Runtime;
using Java.Interop;
using Java.Lang;

namespace ExportConstructorBug
{
    [Activity(Label = "@string/app_name", MainLauncher = true)]
    public class MainActivity : Activity
    {
        protected override void OnCreate(Bundle? savedInstanceState)
        {
            base.OnCreate(savedInstanceState);

            // Simulate a unmanaged/native call of the problematic constructor
            
            var smapleClass = Class.ForName("com.bug.exportconstructor.SampleManagedClass");
            var contextClass = Class.ForName("android.content.Context");

            var constructor = smapleClass.GetConstructor(contextClass);
            try
            {
                constructor.NewInstance(Application.Context);
            }
            catch (System.Exception ex)
            {
                // Will throw a System.NotSupportedException
                // 'Could not activate JNI Handle of Java type 'com/bug/exportconstructor/SampleManagedClass'
                // as managed type 'ExportConstructorBug.SampleManagedClass'.'
                System.Diagnostics.Debug.WriteLine(ex);
                throw;
            }
        }
    }

    [Register("com.bug.exportconstructor.SampleManagedClass")]
    public class SampleManagedClass : Java.Lang.Object
    {
        [Export(SuperArgumentsString = "")]
        public SampleManagedClass(Context context)
        {
            System.Diagnostics.Debug.WriteLine("success");
        }

        public SampleManagedClass(nint handle, JniHandleOwnership transfer)
        {
        }

        public SampleManagedClass()
        {
        }
    }
}

Did you find any workaround?

No, real workaround but you can go to "obj\Debug\net6.0-android\android\src\com\bug\exportconstructor\SampleManagedClass.java" and correct the generated constructor with:

public SampleManagedClass (android.content.Context p0)
{
    super ();
    if (getClass () == SampleManagedClass.class) {
	mono.android.TypeManager.Activate ("ExportConstructorBug.SampleManagedClass, ExportConstructorBug", "Android.Content.Context, Mono.Android", this, new java.lang.Object[] { p0 });
    }
}

Then start the app again. Now the app should run fine and output "success".

However, your edit will be overriden by a clean build.

Relevant log output

[monodroid] Could not activate JNI Handle 0x7ffd2bd94e50 (key_handle 0x65d856e) of Java type 'com/bug/exportconstructor/SampleManagedClass' as managed type 'ExportConstructorBug.SampleManagedClass'.
[monodroid] Java.Interop.JavaLocationException: Exception of type 'Java.Interop.JavaLocationException' was thrown.
[monodroid] Java.Lang.Error: Exception of type 'Java.Lang.Error' was thrown.
[monodroid] 
[monodroid]   --- End of managed Java.Lang.Error stack trace ---
[monodroid] java.lang.Error: Java callstack:
[monodroid] 	at mono.android.TypeManager.n_activate(Native Method)
[monodroid] 	at mono.android.TypeManager.Activate(TypeManager.java:7)
[monodroid] 	at com.bug.exportconstructor.SampleManagedClass.<init>(SampleManagedClass.java:22)
[monodroid] 	at java.lang.reflect.Constructor.newInstance0(Native Method)
[monodroid] 	at java.lang.reflect.Constructor.newInstance(Constructor.java:343)
[monodroid] 	at crc646e3a9ae673c40143.MainActivity.n_onCreate(Native Method)
[monodroid] 	at crc646e3a9ae673c40143.MainActivity.onCreate(MainActivity.java:29)
[monodroid] 	at android.app.Activity.performCreate(Activity.java:8057)
[monodroid] 	at android.app.Activity.performCreate(Activity.java:8037)
[monodroid] 	at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1341)
[monodroid] 	at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3688)
[monodroid] 	at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3864)
[monodroid] 	at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:103)
[monodroid] 	at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
[monodroid] 	at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
[monodroid] 	at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2253)
[monodroid] 	at android.os.Handler.dispatchMessage(Handler.java:106)
[monodroid] 	at android.os.Looper.loopOnce(Looper.java:201)
[monodroid] 	at android.os.Looper.loop(Looper.java:288)
[monodroid] 	at android.app.ActivityThread.main(ActivityThread.java:7870)
[monodroid] 	at java.lang.reflect.Method.invoke(Native Method)
[monodroid] 	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
[monodroid] 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)

Metadata

Metadata

Assignees

Labels

Area: BindingsIssues in Java Library Binding projects.needs-triageIssues that need to be assigned.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions