From 0e96bcf1d5327c255a94caf48884b8bf20562f5a Mon Sep 17 00:00:00 2001 From: Ameer Armaly Date: Tue, 18 Jan 2022 17:40:59 -0800 Subject: [PATCH] TouchInteractionController: cache motion events when transitioning between states. When transitioning between states, TouchInteractionController will queue motion events and not deliver them until the state transition has been confirmed. This avoids the case where a service will request a state transition twice because it processes a motion event after sending the transition request but before the transition completes. Bug: 159651900 Test: atest TouchInteractionControllerTest Change-Id: I2437b635a8053de28212d93e67c7144134c1dc08 --- .../TouchInteractionController.java | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/core/java/android/accessibilityservice/TouchInteractionController.java b/core/java/android/accessibilityservice/TouchInteractionController.java index a8ba1d33f3b40..bb2b8d45fd619 100644 --- a/core/java/android/accessibilityservice/TouchInteractionController.java +++ b/core/java/android/accessibilityservice/TouchInteractionController.java @@ -24,6 +24,8 @@ import android.util.ArrayMap; import android.view.MotionEvent; import android.view.accessibility.AccessibilityInteractionClient; +import java.util.LinkedList; +import java.util.Queue; import java.util.concurrent.Executor; /** @@ -102,6 +104,11 @@ public final class TouchInteractionController { private boolean mServiceDetectsGestures; /** Map of callbacks to executors. Lazily created when adding the first callback. */ private ArrayMap mCallbacks; + // A list of motion events that should be queued until a pending transition has taken place. + private Queue mQueuedMotionEvents = new LinkedList<>(); + // Whether this controller is waiting for a state transition. + // Motion events will be queued and sent to listeners after the transition has taken place. + private boolean mStateChangeRequested = false; // The current state of the display. private int mState = STATE_CLEAR; @@ -169,6 +176,14 @@ public final class TouchInteractionController { * main thread. */ void onMotionEvent(MotionEvent event) { + if (mStateChangeRequested) { + mQueuedMotionEvents.add(event); + } else { + sendEventToAllListeners(event); + } + } + + private void sendEventToAllListeners(MotionEvent event) { final ArrayMap entries; synchronized (mLock) { // callbacks may remove themselves. Perform a shallow copy to avoid concurrent @@ -209,6 +224,10 @@ public final class TouchInteractionController { callback.onStateChanged(state); } } + mStateChangeRequested = false; + while (mQueuedMotionEvents.size() > 0) { + sendEventToAllListeners(mQueuedMotionEvents.poll()); + } } /** @@ -253,6 +272,7 @@ public final class TouchInteractionController { } catch (RemoteException re) { throw new RuntimeException(re); } + mStateChangeRequested = true; } } @@ -281,6 +301,7 @@ public final class TouchInteractionController { } catch (RemoteException re) { throw new RuntimeException(re); } + mStateChangeRequested = true; } } @@ -302,6 +323,7 @@ public final class TouchInteractionController { } catch (RemoteException re) { throw new RuntimeException(re); } + mStateChangeRequested = true; } }