Bug 60902 - JNIEnv Bindings crash for nested arrays with null values
Summary: JNIEnv Bindings crash for nested arrays with null values
Status: NEW
Alias: None
Product: Android
Classification: Xamarin
Component: Bindings ()
Version: 8.0 (15.4)
Hardware: PC Windows
: --- normal
Target Milestone: ---
Assignee: Atsushi Eno
URL:
Depends on:
Blocks:
 
Reported: 2017-11-28 21:47 UTC by Sylvain Gravel
Modified: 2017-11-28 21:47 UTC (History)
1 user (show)

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

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 for Bug 60902 on Developer Community or GitHub if you have new information to add and do not yet see a matching new report.

If the latest results still closely match this report, you can use the original description:

  • Export the original title and description: Developer Community HTML or GitHub Markdown
  • Copy the title and description into the new report. Adjust them to be up-to-date if needed.
  • Add your new information.

In special cases on GitHub you might also want the comments: GitHub Markdown with public comments

Related Links:
Status:
NEW

Description Sylvain Gravel 2017-11-28 21:47:25 UTC
I am integrating ExoPlayer2 into our app using the latest ExoPlayer2 bindings from the Nuget package (version 2.6.0 from this link https://github.com/martijn00/ExoPlayerXamarin).  Note that I'm the one who ported the bindings to version 2.6.0.

In my app, I need to create a custom TrackSelector for offline playback of downloaded streams, here is my code :


```
using Com.Google.Android.Exoplayer2;
using Com.Google.Android.Exoplayer2.Source;
using Com.Google.Android.Exoplayer2.Trackselection;
using WhateverProject.Droid.Services;

namespace WhateverProject.Droid
{
    public class OfflineTrackSelector : MappingTrackSelector
    {
        private DownloadTask download;

        public OfflineTrackSelector(DownloadTask download)
        {
            this.download = download;
        }
        
        protected override ITrackSelection[] SelectTracks(IRendererCapabilities[] capabilities, TrackGroupArray[] trackGroups, int[][][] formatSupports)
        {
            ITrackSelection[] result = new ITrackSelection[capabilities.Length];

            for (int i = 0; i <= result.Length; ++i)
            {
                if (i < result.Length)
                {
                    IRendererCapabilities curentCaps = capabilities[i];

                    switch (curentCaps.TrackType)
                    {
                        case C.TrackTypeVideo:
                        {
                            result[i] = FindVideoTrackSelection(trackGroups[i]);
                            break;
                        }
                        default:
                        {
                            result[i] = GetFirstTrackSelection(trackGroups[i]);
                            break;
                        }
                    }
                }
            }

            return result;
        }

        private ITrackSelection FindVideoTrackSelection(TrackGroupArray trackGroup)
        {
            int trackIndex = this.download.VideoRepresentationKey.RepresentationIndex;

            // We only expect one video adaptation set
            return new FixedTrackSelection(trackGroup.Get(0), trackIndex);
        }

        private ITrackSelection GetFirstTrackSelection(TrackGroupArray trackGroup)
        {
            if (trackGroup.Length > 0 &&
                trackGroup.Get(0).Length > 0)
            {
                // First available track, if there is one
                return new FixedTrackSelection(trackGroup.Get(0), 0);
            }

            return null;
        }
    }
}
```

The crash occurs upon return of `SelectTracks` because of the `int[][][] formatSupports` parameter.  In our specific use-case, this array is of size `int[5][][]` and the last nested array contains 2 null pointers :

```
-		formatSupports	{int[5][][]}	int[][][]
+		[0]	{int[1][]}	int[][]
+		[1]	{int[1][]}	int[][]
+		[2]	{int[0][]}	int[][]
+		[3]	{int[0][]}	int[][]
-		[4]	{int[2][]}	int[][]
		[0]	(null)	object
		[1]	(null)	object
```

In JNIEnv, this causes issues on line 1437 because where are trying to get the type of a null pointer for `_source`, our null array pointers. (https://github.com/xamarin/xamarin-android/blob/595186928ae4d79fab396dc05e7dc2750e808544/src/Mono.Android/Android.Runtime/JNIEnv.cs#L1437)

The crash output/stack is as follows :
```
11-28 16:30:46.113 I/MonoDroid( 7834): System.NullReferenceException: Object reference not set to an instance of an object.
11-28 16:30:46.113 I/MonoDroid( 7834):   at Android.Runtime.JNIEnv+<>c.<CreateCopyManagedToNativeArray>b__114_10 (System.Array source, System.IntPtr dest) [0x0001b] in <e975227ac8644a30bb0866117325de0d>:0 
11-28 16:30:46.113 I/MonoDroid( 7834):   at Android.Runtime.JNIEnv.CopyArray (System.Array source, System.Type elementType, System.IntPtr dest) [0x00042] in <e975227ac8644a30bb0866117325de0d>:0 
11-28 16:30:46.113 I/MonoDroid( 7834):   at Android.Runtime.JNIEnv+<>c.<CreateCopyManagedToNativeArray>b__114_10 (System.Array source, System.IntPtr dest) [0x0002b] in <e975227ac8644a30bb0866117325de0d>:0 
11-28 16:30:46.113 I/MonoDroid( 7834):   at Android.Runtime.JNIEnv.CopyArray (System.Array source, System.Type elementType, System.IntPtr dest) [0x00042] in <e975227ac8644a30bb0866117325de0d>:0 
11-28 16:30:46.113 I/MonoDroid( 7834):   at Android.Runtime.JNIEnv.CopyArray[T] (T[] src, System.IntPtr dest) [0x0000e] in <e975227ac8644a30bb0866117325de0d>:0 
11-28 16:30:46.113 I/MonoDroid( 7834):   at Com.Google.Android.Exoplayer2.Trackselection.MappingTrackSelector.n_SelectTracks_arrayLcom_google_android_exoplayer2_RendererCapabilities_arrayLcom_google_android_exoplayer2_source_TrackGroupArray_arrayarrayarrayI (System.IntPtr jnienv, System.IntPtr native__this, System.IntPtr native_p0, System.IntPtr native_p1, System.IntPtr native_p2) [0x00076] in <7f5305916fcf4ef4840ff4bdd03ad80a>:0 
11-28 16:30:46.113 I/MonoDroid( 7834):   at (wrapper dynamic-method) System.Object:ed2b6ab5-4bf7-4b71-8f32-826912f725eb (intptr,intptr,intptr,intptr,intptr)
```


I have confirmed this is indeed the issue by forcing empty arrays to prevent the null pointer :

```        
        protected override ITrackSelection[] SelectTracks(IRendererCapabilities[] capabilities, TrackGroupArray[] trackGroups, int[][][] formatSupports)
        {
            ITrackSelection[] result = new ITrackSelection[capabilities.Length];

            for (int i = 0; i <= result.Length; ++i)
            {
                if (i < result.Length)
                {
                    IRendererCapabilities curentCaps = capabilities[i];

                    switch (curentCaps.TrackType)
                    {
                        case C.TrackTypeVideo:
                        {
                            result[i] = FindVideoTrackSelection(trackGroups[i]);
                            break;
                        }
                        default:
                        {
                            result[i] = GetFirstTrackSelection(trackGroups[i]);
                            break;
                        }
                    }
                }
                else if(i < formatSupports.Length)
                {
                    // Fix bindings crash...
                    for (int j = 0; j < formatSupports[i].Length; ++j)
                    {
                        formatSupports[i][j] = new int[] { };
                    }
                }
            }

            return result;
        }
```

Of course, this doesn't work as Xamarin later tries to find a Mono-only object's JNI representation but it does confirm my doubts about the bug.  Here is the crash output after running the "fixed" code :

```
11-28 16:35:04.125 I/MonoDroid( 9561): System.ArgumentException: Handle must be valid.
11-28 16:35:04.135 I/MonoDroid( 9561): Parameter name: instance
11-28 16:35:04.135 I/MonoDroid( 9561):   at Java.Interop.JniEnvironment+Types.GetObjectClass (Java.Interop.JniObjectReference instance) [0x00009] in <7cfbebb561c54efc9010b018c0846c7e>:0 
11-28 16:35:04.135 I/MonoDroid( 9561):   at Android.Runtime.JNIEnv.GetObjectClass (System.IntPtr jobject) [0x00007] in <e975227ac8644a30bb0866117325de0d>:0 
11-28 16:35:04.135 I/MonoDroid( 9561):   at Android.Runtime.JNIEnv.AssertCompatibleArrayTypes (System.Type sourceType, System.IntPtr destArray) [0x00007] in <e975227ac8644a30bb0866117325de0d>:0 
11-28 16:35:04.135 I/MonoDroid( 9561):   at Android.Runtime.JNIEnv.CopyArray (System.Array source, System.Type elementType, System.IntPtr dest) [0x00030] in <e975227ac8644a30bb0866117325de0d>:0 
11-28 16:35:04.135 I/MonoDroid( 9561):   at Android.Runtime.JNIEnv+<>c.<CreateCopyManagedToNativeArray>b__114_10 (System.Array source, System.IntPtr dest) [0x0002b] in <e975227ac8644a30bb0866117325de0d>:0 
11-28 16:35:04.135 I/MonoDroid( 9561):   at Android.Runtime.JNIEnv.CopyArray (System.Array source, System.Type elementType, System.IntPtr dest) [0x00042] in <e975227ac8644a30bb0866117325de0d>:0 
11-28 16:35:04.135 I/MonoDroid( 9561):   at Android.Runtime.JNIEnv+<>c.<CreateCopyManagedToNativeArray>b__114_10 (System.Array source, System.IntPtr dest) [0x0002b] in <e975227ac8644a30bb0866117325de0d>:0 
11-28 16:35:04.135 I/MonoDroid( 9561):   at Android.Runtime.JNIEnv.CopyArray (System.Array source, System.Type elementType, System.IntPtr dest) [0x00042] in <e975227ac8644a30bb0866117325de0d>:0 
11-28 16:35:04.135 I/MonoDroid( 9561):   at Android.Runtime.JNIEnv.CopyArray[T] (T[] src, System.IntPtr dest) [0x0000e] in <e975227ac8644a30bb0866117325de0d>:0 
11-28 16:35:04.135 I/MonoDroid( 9561):   at Com.Google.Android.Exoplayer2.Trackselection.MappingTrackSelector.n_SelectTracks_arrayLcom_google_android_exoplayer2_RendererCapabilities_arrayLcom_google_android_exoplayer2_source_TrackGroupArray_arrayarrayarrayI (System.IntPtr jnienv, System.IntPtr native__this, System.IntPtr native_p0, System.IntPtr native_p1, System.IntPtr native_p2) [0x00076] in <7f5305916fcf4ef4840ff4bdd03ad80a>:0 
11-28 16:35:04.135 I/MonoDroid( 9561):   at (wrapper dynamic-method) System.Object:b86647d0-e722-4335-b131-64b6f3869673 (intptr,intptr,intptr,intptr,intptr)
```

Thanks,
SG