Bug 8765 - Crash happen when implement custom scrollview
Summary: Crash happen when implement custom scrollview
Status: RESOLVED INVALID
Alias: None
Product: Android
Classification: Xamarin
Component: BCL Class Libraries ()
Version: 4.2.x
Hardware: PC Windows
: --- normal
Target Milestone: ---
Assignee: Bugzilla
URL:
Depends on:
Blocks:
 
Reported: 2012-12-05 04:45 UTC by Low
Modified: 2012-12-05 21:06 UTC (History)
2 users (show)

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


Attachments
Crash logcat (339.86 KB, text/plain)
2012-12-05 04:45 UTC, Low
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:
RESOLVED INVALID

Description Low 2012-12-05 04:45:00 UTC
Created attachment 3058 [details]
Crash logcat

When I implement a custom scrollview and override onTouch event, sometimes it will crash. Refer to the attachment for the crash logcat. This crash issue is happen SOMETIMES only. I need to keep on touch and scroll the screen for sometime then only it will crash. Please help to look into this issue.

Activity code:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.Util;

namespace HelloM4A
{
    [Activity (Label = "Activity2", MainLauncher = true)]            
    public class Activity2 : Activity
    {
        protected override void OnCreate (Bundle bundle)
        {
            base.OnCreate (bundle);

            SetContentView(Resource.Layout.Main);

            LinearLayout v = (LinearLayout)FindViewById(Resource.Id.layout);
            CustomScrollView scrollView = new CustomScrollView(this);
            LinearLayout ll = new LinearLayout(this);
            TextView tv = new TextView(this);
            tv.Text = "aksdjhaskudhasiurgqwkebegbsewkrhqwakdnkabsrkawdksbjfbadaskfbaskdhasl;dasdkbaskdbasdhasldhnasldhqaowehqwkjfbkgfbeklrhwqaohlawndsl;dhiaowhrwqan";
            ll.AddView(tv);
            scrollView.AddView(ll, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MatchParent, ViewGroup.LayoutParams.MatchParent));
            v.AddView(scrollView, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MatchParent, ViewGroup.LayoutParams.MatchParent));
        }
        

        class CustomScrollView : ScrollView
        {
            int mMarginThreshold;
            
            public CustomScrollView(Context context)
                : base(context)
            {
                mMarginThreshold = (int)TypedValue.ApplyDimension(ComplexUnitType.Dip,
                                                                  40, this.Resources.DisplayMetrics);
            }
            
            public override bool OnTouchEvent(MotionEvent ev)
            {
                int x = (int)ev.GetX();
                Console.WriteLine("x: " + x + ", margin: " + mMarginThreshold);
                Console.WriteLine("x: " + ev.GetX() + ", y: " + ev.GetY() + ", index: " + ev.ActionIndex);
                if (x <= mMarginThreshold)
                {
                    return false;
                }
                
                return base.OnTouchEvent(ev);
            }
        }
    }
}


---------------------------------------------------------
Layout file:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:id="@+id/layout"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
</LinearLayout>
Comment 1 Jonathan Pryor 2012-12-05 16:31:03 UTC
The cause for the crash is in the logcat output you attached:

> UNHANDLED EXCEPTION: Java.Lang.IllegalArgumentException: Exception of type 'Java.Lang.IllegalArgumentException' was thrown.
> at Android.Runtime.JNIEnv.CallNonvirtualBooleanMethod (intptr,intptr,intptr,Android.Runtime.JValue[]) <0x00090>
> at Android.Views.View.OnTouchEvent (Android.Views.MotionEvent) <0x001e7>
> at HelloM4A.Activity2/CustomScrollView.OnTouchEvent (Android.Views.MotionEvent) <0x0029f>
> at Android.Views.View.n_OnTouchEvent_Landroid_view_MotionEvent_ (intptr,intptr,intptr) <0x00057>
> at (wrapper dynamic-method) object.bafd36d0-07bf-490c-960a-0a5f29f00733 (intptr,intptr,intptr) <0x00033>
>   --- End of managed exception stack trace ---
> java.lang.IllegalArgumentException: pointerIndex out of range
> 	at android.view.MotionEvent.nativeGetAxisValue(Native Method)
> 	at android.view.MotionEvent.getY(MotionEvent.java:1996)
> 	at android.widget.ScrollView.onTouchEvent(ScrollView.java:588)
> 	at hellom4a.Activity2_CustomScrollView.n_onTouchEvent(Native Method)

From the stack trace, we see that your `base.OnTouchEvent(ev)` invokes ScrollView.onTouchEvent():

https://github.com/android/platform_frameworks_base/blob/master/core/java/android/widget/ScrollView.java#L566

ScrollView.onTouchEvent() then calls MotionEvent.getY(), which calls MotionEvent.nativeGetAxisValue():

https://github.com/android/platform_frameworks_base/blob/master/core/java/android/view/MotionEvent.java#L1824
https://github.com/android/platform_frameworks_base/blob/master/core/jni/android_view_MotionEvent.cpp#L618
https://github.com/android/platform_frameworks_base/blob/master/core/jni/android_view_MotionEvent.cpp#L156

The C++ `pointerIndex` parameter is always `0` (from the Java invocation), and the C++ `event->getPointerCount()` invocation should (hopefully!) be identical to the Java MotionEvent.getPointerCount() method.

In short, you get the IllegalArgumentException because (somehow...) MotionEvent.getPointerCount() is returning 0, thus causing `size_t(0) >= pointerCount` to fail.

To confirm, please add a debug message to your OnTouchEvent() and print out the value of `ev.PointerCount`: is it ever 0?

If it is ever 0, this is clearly an Android bug -- it should never be 0, especially since they document that it's always >= 1:

http://developer.android.com/reference/android/view/MotionEvent.html#getPointerCount()
Comment 2 Low 2012-12-05 21:06:49 UTC
Updated onTouchEvent methods to the following:

public override bool OnTouchEvent(MotionEvent ev)
{
	int x = (int)ev.GetX();
	if (x <= mMarginThreshold)
	{
		return false;
	}

	bool flag = false;
	try
	{
		flag = base.OnTouchEvent(ev);
	}
	catch(Java.Lang.IllegalArgumentException e)
	{
		Console.WriteLine("Execption catch. pointer count: " + ev.PointerCount);
		Console.WriteLine("Execption catch. action index: " + ev.ActionIndex);
		int activePointerId = MotionEventCompat.GetActionIndex(ev);
		int activePointerIndex = MotionEventCompat.FindPointerIndex(ev, activePointerId);

		Console.WriteLine("Execption catch. pointer id: " + activePointerId);
		Console.WriteLine("Execption catch. pointer index: " + activePointerIndex);

		Console.WriteLine("Exception: " + e.Message);
	}

	return flag;
}


---------------------------------------------------------------------------
And I get the output of:

Execption catch. pointer count: 2
Execption catch. action index: 0
Execption catch. pointer id: 0
Execption catch. pointer index: 0
Exception: pointerIndex out of range
Execption catch. pointer count: 2
Execption catch. action index: 0
Execption catch. pointer id: 0
Execption catch. pointer index: 0
Exception: pointerIndex out of range
Execption catch. pointer count: 2
Execption catch. action index: 0
Execption catch. pointer id: 0
Execption catch. pointer index: 0
Exception: pointerIndex out of range
Execption catch. pointer count: 1
Execption catch. action index: 0
Execption catch. pointer id: 0
Execption catch. pointer index: -1
Exception: pointerIndex out of range

It seems like Android is throwing exception even though the pointer count is not 0. And I have test it with native Android code, it seems to be crash on the same point. So, this is confirm to be an Android bug. So for now, I will catch the exception in order to prevent crash.