summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Siarhei Vishniakou <svv@google.com> 2023-07-19 16:37:28 -0700
committer Siarhei Vishniakou <svv@google.com> 2023-07-21 16:53:59 +0000
commitd472dc84ce789689950cccd9d932511b0f93b67c (patch)
tree1c96499b11d0d2fd0574c86e003b336ea890e05a
parent86a2352b07974f8f42e3c8a5f3295805139f5aaf (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.java43
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 {