Bug 15891 - Android/Java methods that take a Set/Map/List as an argument cause various exceptions, IncompatibleClassChangeError, ArgumentNullException...
Summary: Android/Java methods that take a Set/Map/List as an argument cause various ex...
Status: VERIFIED FIXED
Alias: None
Product: Android
Classification: Xamarin
Component: Bindings ()
Version: 4.10.0.x
Hardware: All All
: Normal normal
Target Milestone: 4.12.0 (KitKat)
Assignee: Atsushi Eno
URL:
: 13141 16828 ()
Depends on:
Blocks:
 
Reported: 2013-11-02 17:03 UTC by T.J. Purtell
Modified: 2014-02-10 01:49 UTC (History)
9 users (show)

Tags:
Is this bug a regression?: ---
Last known good build:


Attachments
Code that calls IntentFilter.matchCategories to generate the crash in the runtime binding (2.17 KB, text/plain)
2013-11-02 17:03 UTC, T.J. Purtell
Details


Notice (2018-05-24): bugzilla.xamarin.com is now in read-only mode.

Please join us on Visual Studio Developer Community and in the Xamarin and Mono organizations on GitHub to continue tracking issues. Bugzilla will remain available for reference in read-only mode. We will continue to work on open Bugzilla bugs, copy them to the new locations as needed for follow-up, and add the new items under Related Links.

Our sincere thanks to everyone who has contributed on this bug tracker over the years. Thanks also for your understanding as we make these adjustments and improvements for the future.


Please create a new report on Developer Community or GitHub with your current version information, steps to reproduce, and relevant error messages or log files if you are hitting an issue that looks similar to this resolved bug and you do not yet see a matching new report.

Related Links:
Status:
VERIFIED FIXED

Description T.J. Purtell 2013-11-02 17:03:59 UTC
Created attachment 5320 [details]
Code that calls IntentFilter.matchCategories to generate the crash in the runtime binding

The binding generated for the Android runtime library seems to use the passed in argument rather than the temporary JavaSet it created when invoking the method via JNI.  This error originally hit me in a binding project, but instances of the issue occur inside the Xamarin.Android runtime as well.  It may happen in stable too, but I haven't tried it there. 

==== EXCEPTION ====
11-02 13:53:06.854: I/MonoDroid(4655): UNHANDLED EXCEPTION: Java.Lang.IncompatibleClassChangeError: Exception of type 'Java.Lang.IncompatibleClassChangeError' was thrown.
11-02 13:53:06.854: I/MonoDroid(4655): at Android.Runtime.JNIEnv.CallObjectMethod (intptr,intptr,Android.Runtime.JValue[]) <0x00128>
11-02 13:53:06.854: I/MonoDroid(4655): at Android.Content.IntentFilter.MatchCategories (System.Collections.Generic.ICollection`1<string>) <0x00127>
11-02 13:53:06.854: I/MonoDroid(4655): at JavaBindingForSetsBroken.Activity1.OnCreate (Android.OS.Bundle) <0x00193>
11-02 13:53:06.854: I/MonoDroid(4655): at Android.App.Activity.n_OnCreate_Landroid_os_Bundle_ (intptr,intptr,intptr) <0x0005b>
11-02 13:53:06.854: I/MonoDroid(4655): at (wrapper dynamic-method) object.d40c269e-d657-4034-9246-3a3abb0966a1 (intptr,intptr,intptr) <0x00043>
11-02 13:53:06.854: I/MonoDroid(4655):   --- End of managed exception stack trace ---
11-02 13:53:06.854: I/MonoDroid(4655): java.lang.IncompatibleClassChangeError: interface not implemented
11-02 13:53:06.854: I/MonoDroid(4655): 	at android.content.IntentFilter.matchCategories(IntentFilter.java:1030)
11-02 13:53:06.854: I/MonoDroid(4655): 	at javabindingforsetsbroken.Activity1.n_onCreate(Native Method)
11-02 13:53:06.854: I/MonoDroid(4655): 	at javabindingforsetsbroken.Activity1.onCreate(Activity1.java:28)
11-02 13:53:06.854: I/MonoDroid(4655): 	at android.app.Activity.performCreate(Activity.java:5008)
11-02 13:53:06.854: I/MonoDroid(4655): 	at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1079)
11-02 13:53:06.854: I/MonoDroid(4655): 	at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2056)
11-02 13:53:06.854: I/MonoDroid(4655): 	at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2117)
11-02 13:53:06.854: I/MonoDroid(4655): 	at android.app.ActivityThread.access$600(ActivityThread.java:139)
11-02 13:53:06.854: I/MonoDroid(4655): 	at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1207)
11-02 13:53:06.854: I/MonoDroid(4655): 	at android.os.Handler.dispatchMessage(Handler.java:99)
11-02 13:53:06.854: I/MonoDroid(4655): 	at android.os.Looper.loop(Looper.java:137)
11-02 13:53:06.854: I/MonoDroid(4655): 	at android.app.ActivityThread.main(ActivityThread.java:4822)
11-02 13:53:06.854: I/MonoDroid(4655): 	at java.lang.reflect.Method.invokeNative(Native Method)
11-02 13:53:06.854: I/MonoDroid(4655): 	at java.lang.reflect.Method.invoke(Method.java:511)
11-02 13:53:06.854: I/MonoDroid(4655): 	at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:789)
11-02 13:53:06.854: I/MonoDroid(4655): 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:556)
11-02 13:53:06.854: I/MonoDroid(4655): 	at dalvik.system.NativeStart.main(Native Method)

==== CODE ====
    [Register("matchCategories", "(Ljava/util/Set;)Ljava/lang/String;", "")]
    public string MatchCategories(ICollection<string> categories)
    {
      if (IntentFilter.id_matchCategories_Ljava_util_Set_ == IntPtr.Zero)
        IntentFilter.id_matchCategories_Ljava_util_Set_ = JNIEnv.GetMethodID(IntentFilter.class_ref, "matchCategories", "(Ljava/util/Set;)Ljava/lang/String;");
      IntPtr jobject = JavaSet<string>.ToLocalJniHandle(categories);
      IntPtr handle = this.Handle;
      IntPtr jmethod = IntentFilter.id_matchCategories_Ljava_util_Set_;
      JValue[] jvalueArray = new JValue[1];
      int index = 0;
      
      /// this
      //jvalueArray[index] = new JValue((IJavaObject) JavaObjectExtensions.ToInteroperableCollection<string>(categories));
      /// should be this (otherwise there is a class cast exception)
      jvalueArray[index] = new JValue(jobject);

      string @string = JNIEnv.GetString(JNIEnv.CallObjectMethod(handle, jmethod, jvalueArray), JniHandleOwnership.TransferLocalRef);
      JNIEnv.DeleteLocalRef(jobject);
      return @string;
    }
Comment 1 T.J. Purtell 2013-11-03 13:52:58 UTC
The same issue also applies for Java methods that take a HashMap, with the corresponding .NET bound type being an IDictionary<x>
Comment 2 T.J. Purtell 2013-11-03 13:58:08 UTC
For IDictionary, the temporary is created and not used.  The conversion with the JavaObjectExtensions.ToInteroperableCollection causes issues if a null was passed in, where the  JavaDictionary<x>.ToLocalJniHandle allows nulls correctly.
Comment 3 T.J. Purtell 2013-11-26 14:29:13 UTC
Another instance of binding issues related to collection handling issues similar to these is on the forums: http://forums.xamarin.com/discussion/6477/null-allowableaccounts-arg-accountmanager-newchooseaccountintent-causes-exception-in-xamarin-4-8#latest

AccountManager.NewChooseAccountIntent fails if a null is passed to the second argument which is allowed to be null according to the Android API.
Comment 4 Atsushi Eno 2013-12-11 08:59:26 UTC
Thanks for the detailed analysis, it helped us a lot! This should be fixed in the next-to-next apha release.

[master d3de31f]
Comment 5 Jonathan Pryor 2013-12-11 13:44:30 UTC
The master/d3de31f fix didn't actually fix the ArgumentNullException.

The ArgumentNullException is fixed in monodroid/555e29e2.

I still don't understand what would prompt the IncompatibleClassChangeError exception.
Comment 6 T.J. Purtell 2013-12-11 13:47:17 UTC
It's been awhile now, so I am going off my memory, but I think JavaObjectExtensions.ToInteroperableCollection returns something that conforms to the List interface not the Set interface.  The generated binding code is passing the "not quite so interoperable collection" instead of the result of JavaSet<string>.ToLocalJniHandle(categories) which should work correctly.
Comment 7 Jonathan Pryor 2013-12-11 13:57:21 UTC
The java.lang.IncompatibleClassChangeError is still present. :-(
Comment 8 Jonathan Pryor 2013-12-11 13:58:27 UTC
@T.J.: monodroid/555e29e2 fixes things by nuking JavaObjectExtensions.ToInteroperableCollection() from the generated code. :-)

But that just solves the ArgumentNullException issue (if you actually try to pass `null` to a method), not the IncompatibleClassChangeError issue.
Comment 9 Jonathan Pryor 2013-12-11 14:10:12 UTC
Actually, monodroid/555e29e2 does fix the IncompatibleClassChangeError. My attempt to test it was using an unfixed Mono.Android.dll. Doh!
Comment 10 Jonathan Pryor 2013-12-16 16:30:26 UTC
*** Bug 16828 has been marked as a duplicate of this bug. ***
Comment 11 Jonathan Pryor 2014-01-10 16:06:21 UTC
*** Bug 13141 has been marked as a duplicate of this bug. ***
Comment 12 Sadik Ali 2014-02-06 06:11:01 UTC
I have added attached code into sample android project, but I am getting errors:  

Android.Content.IntentFilter does not contain definition for "id_matchCategories_Ljava_util_Set_ ", "id_matchCategories_Ljava_util_Set_ " and "id_matchCategories_Ljava_util_Set_" 

Refer screen shot: http://screencast.com/t/MlQb3IgPO

Please suggest me what assembly I need to add to verify this issue.
Comment 13 Jonathan Pryor 2014-02-06 11:00:24 UTC
@Sadik: This bug is a binding bug, in that the binding of IntentFilter.MatchCategories() was broken. The MatchCategories() method provided in Comment #0 was a result of decompiling Mono.Android.dll, and cannot be used as-is outside of Mono.Android.dll (because IntentFilter.id_matchCategories_Ljava_util_Set_ is `private`, among other reasons).

If you can call IntentFilter.MatchCategories() without getting an ArgumentNullException or IncompatibleClassChangeError, the bug has been fixed.
Comment 14 Sadik Ali 2014-02-07 03:56:37 UTC
Thanks Jonathan for your suggestion.

I have verified this issue by following steps:

1. Created android sample project.
2. Added code:

  var a_set = new HashSet<string>();
  var filter = new IntentFilter(Intent.ActionView);
  var results = filter.MatchCategories(a_set);
3. Debug application successfully.
4. To verify JavaObjectExtensions.ToInteroperableCollection issue added below code:

var dict = new Dictionary<string, string>();
JavaObjectExtensions.ToInteroperableCollection<String, String> (dict);

5. Debug successfully

But when I debug using below code getting ambiguity errors:
 
var dict = new Dictionary<string, string>();
JavaObjectExtensions.ToInteroperableCollection(dict);

Refer screen shot: http://screencast.com/t/xS92aMWY

Checked With:
All Mac, Windows
XS 4.2.3(Build 54) f3fd2a8582693cd1728166bcfc17056235cb5b05
Xamarin addins: b992974cc7bee7713bba6abaf2b4fac94e67dd1c
XA 4.12.0.22

Please let me know if this is issue
Comment 15 Jonathan Pryor 2014-02-07 11:33:47 UTC
> But when I debug using below code getting ambiguity errors:
> Please let me know if this is issue

That is not an issue. The JavaObjectExtensions.ToInteroperableCollection() methods are _obsolete_, and should not be used (and the fix entailed REMOVING their use from the binding).
Comment 16 Sadik Ali 2014-02-10 01:49:43 UTC
As per comment 14 and comment 15, I marked this as a verified