From 79ae710cc54b5872ad4a41e67e3cd9b73b20ff5b Mon Sep 17 00:00:00 2001 From: Alex Danoff Date: Thu, 8 Jun 2023 03:34:43 -0700 Subject: [PATCH] W3CPointerEvents: fix NPE due to missing eventCoords (#37761) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/37761 Changelog: [Android] [Fixed] - W3CPointerEvents: fix a case where cancel can cause NPE When we need to dispatch a pointercancel event, we clear the previous state (last coordinates, last hit path) for the active pointer ID (this is needed in order to fire the proper events when the action causing the cancel is over). Previously, we stored the previous state in the form of references to the corresponding state in the current `PointerEventState`. However, this PointerEventState is also used later when actually dispatching the events. Since the dispatch can happen asynchronously, alterations to the event state might be visible during the dispatch. In particular, we saw some cases where an NPE can occur due to removing entries from our local state (which got reflected in the event state). This change fixes the issue by making copies of the data from the PointerEventState instead of just storing references to it. Reviewed By: rozele Differential Revision: D46522585 fbshipit-source-id: 78d6dd60cf8f5419870fb9f6703443c957d4d048 --- .../com/facebook/react/uimanager/JSPointerDispatcher.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/JSPointerDispatcher.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/JSPointerDispatcher.java index 13701c36f65..0f1a206ba0b 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/JSPointerDispatcher.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/JSPointerDispatcher.java @@ -89,8 +89,10 @@ public class JSPointerDispatcher { private void updatePreviousStateFromEvent(MotionEvent event, PointerEventState eventState) { // Caching the event state so we have a new "last" - mLastHitPathByPointerId = eventState.getHitPathByPointerId(); - mLastEventCoordinatesByPointerId = eventState.getEventCoordinatesByPointerId(); + // note: we need to make copies here as the eventState may be accessed later and we don't want + // mutations of these instance vars to affect it + mLastHitPathByPointerId = new HashMap<>(eventState.getHitPathByPointerId()); + mLastEventCoordinatesByPointerId = new HashMap<>(eventState.getEventCoordinatesByPointerId()); mLastButtonState = event.getButtonState(); // Clean up any stale pointerIds