diff options
| author | 2023-07-19 16:37:28 -0700 | |
|---|---|---|
| committer | 2023-07-21 16:53:59 +0000 | |
| commit | d472dc84ce789689950cccd9d932511b0f93b67c (patch) | |
| tree | 1c96499b11d0d2fd0574c86e003b336ea890e05a | |
| parent | 86a2352b07974f8f42e3c8a5f3295805139f5aaf (diff) | |
Allow sendMotionEvent from any thread
Before this CL, sendMotionEvent was only allowed to be called from the
looper thread. However, this was not enforced in any way. This led to
bugs where the event may not be added to the internal queue of
NativeInputEventSender yet, but the fd is already getting processed and
therefore the received event was dropped without any error.
One option to fix this is to simply raise Exception when the call
happens on the wrong thread. However, that still puts the burden on the
caller to figure out how to call it properly.
Instead, in this CL, allow this method to be called on any thread.
If it's called on the looper thread, then great - return the result
immediately.
If it's called on another thread, then create a Handler and post a
future to the looper thread. Then wait indefinitely until the action is
executed on the looper thread, and return the result in the original
thread.
Bug: 290931418
Test: atest PointerEventDispatcherTest --rerun-until-failure 1000
Change-Id: I39396a8d04e73c090e7d71feafc734075ed27867
| -rw-r--r-- | core/java/android/view/InputEventSender.java | 43 |
1 files changed, 35 insertions, 8 deletions
diff --git a/core/java/android/view/InputEventSender.java b/core/java/android/view/InputEventSender.java index 64f62c75199e..c843bbea561f 100644 --- a/core/java/android/view/InputEventSender.java +++ b/core/java/android/view/InputEventSender.java @@ -18,6 +18,7 @@ package android.view; import android.compat.annotation.UnsupportedAppUsage; import android.os.Build; +import android.os.Handler; import android.os.Looper; import android.os.MessageQueue; import android.util.Log; @@ -25,6 +26,10 @@ import android.util.Log; import dalvik.system.CloseGuard; import java.lang.ref.WeakReference; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.FutureTask; +import java.util.concurrent.RunnableFuture; /** * Provides a low-level mechanism for an application to send input events. @@ -37,10 +42,10 @@ public abstract class InputEventSender { private long mSenderPtr; - // We keep references to the input channel and message queue objects here so that - // they are not GC'd while the native peer of the receiver is using them. + // We keep references to the input channel and message queue objects (indirectly through + // Handler) here so that they are not GC'd while the native peer of the receiver is using them. private InputChannel mInputChannel; - private MessageQueue mMessageQueue; + private Handler mHandler; private static native long nativeInit(WeakReference<InputEventSender> sender, InputChannel inputChannel, MessageQueue messageQueue); @@ -63,9 +68,9 @@ public abstract class InputEventSender { } mInputChannel = inputChannel; - mMessageQueue = looper.getQueue(); + mHandler = new Handler(looper); mSenderPtr = nativeInit(new WeakReference<InputEventSender>(this), - mInputChannel, mMessageQueue); + mInputChannel, looper.getQueue()); mCloseGuard.open("InputEventSender.dispose"); } @@ -98,8 +103,8 @@ public abstract class InputEventSender { nativeDispose(mSenderPtr); mSenderPtr = 0; } + mHandler = null; mInputChannel = null; - mMessageQueue = null; } /** @@ -122,8 +127,8 @@ public abstract class InputEventSender { } /** - * Sends an input event. - * Must be called on the same Looper thread to which the sender is attached. + * Sends an input event. Can be called from any thread. Do not call this if the looper thread + * is blocked! It would cause a deadlock. * * @param seq The input event sequence number. * @param event The input event to send. @@ -140,6 +145,28 @@ public abstract class InputEventSender { return false; } + if (mHandler.getLooper().isCurrentThread()) { + return sendInputEventInternal(seq, event); + } + // This is being called on another thread. Post a runnable to the looper thread + // with the event injection, and wait until it's processed. + final RunnableFuture<Boolean> task = new FutureTask<>(new Callable<Boolean>() { + @Override + public Boolean call() throws Exception { + return sendInputEventInternal(seq, event); + } + }); + mHandler.post(task); + try { + return task.get(); + } catch (InterruptedException exc) { + throw new IllegalStateException("Interrupted while sending " + event + ": " + exc); + } catch (ExecutionException exc) { + throw new IllegalStateException("Couldn't send " + event + ": " + exc); + } + } + + private boolean sendInputEventInternal(int seq, InputEvent event) { if (event instanceof KeyEvent) { return nativeSendKeyEvent(mSenderPtr, seq, (KeyEvent)event); } else { |