Skip to content

Engine may crash when two fingers tap and move on some special devices - Unexpected pointer change #129765

@LoveJello

Description

@LoveJello

Is there an existing issue for this?

Steps to reproduce

  1. flutter create myapp
  2. run app on some special device
  3. do two finger tap and move using trackpad.

Expected results

No Crash

Actual results

App will crash in AndroidTouchProcessor.java, because AssertionError("Unexpected pointer change") is throwed out.
After adding debug log, I find out that ACTION_SCROLL is generated by that devices, just as below:

ACTION_DOWN -> ACTION_MOVE -> ACTION_SCROLL

PS : it looks like that device uses mouse events to simulate two finger tap and move.

06-29 19:19:42.562  4919  4919 V AndroidTouchProcessor: onGenericMotionEvent, event MotionEvent { action=ACTION_HOVER_ENTER, actionButton=0, id[0]=0, x[0]=1296.2535, y[0]=398.5768, toolType[0]=TOOL_TYPE_MOUSE, buttonState=
0, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=628215, downTime=144832, deviceId=7, source=0x2002, displayId=0 }
06-29 19:19:42.567  4919  4919 V AndroidTouchProcessor: onGenericMotionEvent, event MotionEvent { action=ACTION_HOVER_MOVE, actionButton=0, id[0]=0, x[0]=1296.2535, y[0]=398.5768, toolType[0]=TOOL_TYPE_MOUSE, buttonState=0
, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=628215, downTime=144832, deviceId=7, source=0x2002, displayId=0 }
06-29 19:19:42.575  4919  4919 V AndroidTouchProcessor: onGenericMotionEvent, event MotionEvent { action=ACTION_HOVER_EXIT, actionButton=0, id[0]=0, x[0]=1296.2535, y[0]=398.5768, toolType[0]=TOOL_TYPE_MOUSE, buttonState=0
, classification=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=628222, downTime=628222, deviceId=7, source=0x2002, displayId=0 }
06-29 19:19:42.578  4919  4919 V AndroidTouchProcessor: onTouchEvent, event MotionEvent { action=ACTION_DOWN, actionButton=0, id[0]=0, x[0]=1296.2535, y[0]=398.5768, toolType[0]=TOOL_TYPE_MOUSE, buttonState=0, classificati
on=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=628222, downTime=628222, deviceId=7, source=0x2002, displayId=0 }
06-29 19:19:42.673  4919  4919 V AndroidTouchProcessor: onTouchEvent, event MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=0, x[0]=1277.6, y[0]=448.00885, toolType[0]=TOOL_TYPE_MOUSE, buttonState=0, classification
=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=628328, downTime=628222, deviceId=7, source=0x2002, displayId=0 }
06-29 19:19:42.675  4919  4919 V AndroidTouchProcessor: onGenericMotionEvent, event MotionEvent { action=ACTION_SCROLL, actionButton=0, id[0]=0, x[0]=0.0, y[0]=0.0, toolType[0]=TOOL_TYPE_MOUSE, buttonState=0, classificatio
n=NONE, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=628328, downTime=628222, deviceId=7, source=0x2002, displayId=0 }

Code sample

The patch below will solve this issue, and throw new AssertionError is a bad idea... Hope it will help.

From f1dced036755aa9c0860db07a615ee324894f54c Mon Sep 17 00:00:00 2001
Date: Wed, 28 Jun 2023 13:28:21 +0800
Subject: [PATCH 2/2] Fix Unexpected pointer change on Some Special device
(CPA001)

.../android/AndroidTouchProcessor.java        | 26 ++++++++++++++-----
 1 file changed, 20 insertions(+), 6 deletions(-)

diff --git a/shell/platform/android/io/flutter/embedding/android/AndroidTouchProcessor.java b/shell/platform/android/io/flutter/embedding/android/AndroidTouchProcessor.java
index 52715481ad..ca72ffa173 100644
--- a/shell/platform/android/io/flutter/embedding/android/AndroidTouchProcessor.java
+++ b/shell/platform/android/io/flutter/embedding/android/AndroidTouchProcessor.java
@@ -13,9 +13,12 @@ import java.nio.ByteOrder;
 import java.util.HashMap;
 import java.util.Map;
 
+import io.flutter.Log;
+
 /** Sends touch information from Android to Flutter in a format that Flutter understands. */
 public class AndroidTouchProcessor {
 
+  private static final String TAG = "AndroidTouchProcessor";
   // Must match the PointerChange enum in pointer.dart.
   @IntDef({
     PointerChange.CANCEL,
@@ -101,6 +104,7 @@ public class AndroidTouchProcessor {
   }
 
   public boolean onTouchEvent(@NonNull MotionEvent event) {
+    Log.v(TAG, "onTouchEvent, event " + event);
     return onTouchEvent(event, IDENTITY_TRANSFORM);
   }
 
@@ -178,6 +182,9 @@ public class AndroidTouchProcessor {
   public boolean onGenericMotionEvent(@NonNull MotionEvent event) {
     // Method isFromSource is only available in API 18+ (Jelly Bean MR2)
     // Mouse hover support is not implemented for API < 18.
+
+    Log.v(TAG, "onGenericMotionEvent, event " + event);
+
     boolean isPointerEvent =
         Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2
             && event.isFromSource(InputDevice.SOURCE_CLASS_POINTER);
@@ -244,6 +251,7 @@ public class AndroidTouchProcessor {
       buttons = 0;
     }
 
+    int panZoomType = -1;
     boolean isTrackpadPan = ongoingPans.containsKey(event.getPointerId(pointerIndex));
 
     int signalKind =
@@ -256,8 +264,14 @@ public class AndroidTouchProcessor {
     packet.putLong(motionEventId); // motionEventId
     packet.putLong(timeStamp); // time_stamp
     if (isTrackpadPan) {
-      packet.putLong(getPointerChangeForPanZoom(pointerChange)); // change
-      packet.putLong(PointerDeviceKind.TRACKPAD); // kind
+      panZoomType = getPointerChangeForPanZoom(pointerChange);
+      if (panZoomType > 0) {
+        packet.putLong(panZoomType); // change
+        packet.putLong(PointerDeviceKind.TRACKPAD); // kind
+      } else {
+            packet.putLong(pointerChange); // change
+            packet.putLong(pointerKind); // kind
+        }
     } else {
       packet.putLong(pointerChange); // change
       packet.putLong(pointerKind); // kind
@@ -266,7 +280,7 @@ public class AndroidTouchProcessor {
     packet.putLong(event.getPointerId(pointerIndex)); // device
     packet.putLong(0); // pointer_identifier, will be generated in pointer_data_packet_converter.cc.
 
-    if (isTrackpadPan) {
+    if (isTrackpadPan && (panZoomType > 0)) {
       float[] panStart = ongoingPans.get(event.getPointerId(pointerIndex));
       packet.putDouble(panStart[0]);
       packet.putDouble(panStart[1]);
@@ -334,7 +348,7 @@ public class AndroidTouchProcessor {
       packet.putDouble(0.0); // scroll_delta_x
     }
 
-    if (isTrackpadPan) {
+    if (isTrackpadPan && (panZoomType > 0)) {
       float[] panStart = ongoingPans.get(event.getPointerId(pointerIndex));
       packet.putDouble(viewToScreenCoords[0] - panStart[0]);
       packet.putDouble(viewToScreenCoords[1] - panStart[1]);
@@ -347,7 +361,7 @@ public class AndroidTouchProcessor {
     packet.putDouble(1.0); // scale
     packet.putDouble(0.0); // rotation
 
-    if (isTrackpadPan && getPointerChangeForPanZoom(pointerChange) == PointerChange.PAN_ZOOM_END) {
+    if (isTrackpadPan && (panZoomType == PointerChange.PAN_ZOOM_END)) {
       ongoingPans.remove(event.getPointerId(pointerIndex));
     }
   }
@@ -393,7 +407,7 @@ public class AndroidTouchProcessor {
     } else if (pointerChange == PointerChange.UP || pointerChange == PointerChange.CANCEL) {
       return PointerChange.PAN_ZOOM_END;
     }
-    throw new AssertionError("Unexpected pointer change");
+    return -1;
   }

@PointerDeviceKind

2.41.0

Screenshots or Video

Screenshots / Video demonstration

[Upload media here]

Logs

Logs
[Paste your logs here]

Flutter Doctor output

Doctor output
[Paste your output here]

Metadata

Metadata

Assignees

Labels

P2Important issues not at the top of the work listc: fatal crashCrashes that terminate the processe: device-specificOnly manifests on certain devicesr: fixedIssue is closed as already fixed in a newer versionteam-engineOwned by Engine teamtriaged-engineTriaged by Engine team

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions