diff options
3 files changed, 135 insertions, 17 deletions
diff --git a/core/jni/android_view_InputEventSender.cpp b/core/jni/android_view_InputEventSender.cpp index 9746a07a1b77..97fdb43bb4f6 100644 --- a/core/jni/android_view_InputEventSender.cpp +++ b/core/jni/android_view_InputEventSender.cpp @@ -34,6 +34,8 @@ #include "core_jni_helpers.h" +using android::base::Result; + namespace android { // Log debug messages about the dispatch cycle. @@ -197,16 +199,9 @@ status_t NativeInputEventSender::receiveFinishedSignals(JNIEnv* env) { ScopedLocalRef<jobject> senderObj(env, NULL); bool skipCallbacks = false; for (;;) { - uint32_t publishedSeq; - bool handled; - std::function<void(uint32_t seq, bool handled, nsecs_t consumeTime)> callback = - [&publishedSeq, &handled](uint32_t inSeq, bool inHandled, - nsecs_t inConsumeTime) -> void { - publishedSeq = inSeq; - handled = inHandled; - }; - status_t status = mInputPublisher.receiveFinishedSignal(callback); - if (status) { + Result<InputPublisher::Finished> result = mInputPublisher.receiveFinishedSignal(); + if (!result.ok()) { + const status_t status = result.error().code(); if (status == WOULD_BLOCK) { return OK; } @@ -215,7 +210,7 @@ status_t NativeInputEventSender::receiveFinishedSignals(JNIEnv* env) { return status; } - auto it = mPublishedSeqMap.find(publishedSeq); + auto it = mPublishedSeqMap.find(result->seq); if (it == mPublishedSeqMap.end()) { continue; } @@ -225,9 +220,9 @@ status_t NativeInputEventSender::receiveFinishedSignals(JNIEnv* env) { if (kDebugDispatchCycle) { ALOGD("channel '%s' ~ Received finished signal, seq=%u, handled=%s, " - "pendingEvents=%zu.", - getInputChannelName().c_str(), seq, handled ? "true" : "false", - mPublishedSeqMap.size()); + "pendingEvents=%zu.", + getInputChannelName().c_str(), seq, result->handled ? "true" : "false", + mPublishedSeqMap.size()); } if (!skipCallbacks) { @@ -241,8 +236,8 @@ status_t NativeInputEventSender::receiveFinishedSignals(JNIEnv* env) { } env->CallVoidMethod(senderObj.get(), - gInputEventSenderClassInfo.dispatchInputEventFinished, - jint(seq), jboolean(handled)); + gInputEventSenderClassInfo.dispatchInputEventFinished, + static_cast<jint>(seq), static_cast<jboolean>(result->handled)); if (env->ExceptionCheck()) { ALOGE("Exception dispatching finished signal."); skipCallbacks = true; diff --git a/tests/Input/src/com/android/test/input/InputEventAssignerTest.kt b/tests/Input/src/com/android/test/input/InputEventAssignerTest.kt index 2e985fbba269..b9b347b02958 100644 --- a/tests/Input/src/com/android/test/input/InputEventAssignerTest.kt +++ b/tests/Input/src/com/android/test/input/InputEventAssignerTest.kt @@ -43,7 +43,7 @@ fun createMotionEvent(action: Int, eventTime: Long, source: Int): MotionEvent { xPrecision, yPrecision, deviceId, edgeFlags, source, displayId) } -fun createKeyEvent(action: Int, eventTime: Long): KeyEvent { +private fun createKeyEvent(action: Int, eventTime: Long): KeyEvent { val code = KeyEvent.KEYCODE_A val repeat = 0 return KeyEvent(eventTime, eventTime, action, code, repeat) diff --git a/tests/Input/src/com/android/test/input/InputEventSenderAndReceiverTest.kt b/tests/Input/src/com/android/test/input/InputEventSenderAndReceiverTest.kt new file mode 100644 index 000000000000..4f95ce585de2 --- /dev/null +++ b/tests/Input/src/com/android/test/input/InputEventSenderAndReceiverTest.kt @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.test.input + +import android.os.HandlerThread +import android.os.Looper +import android.view.InputChannel +import android.view.InputEvent +import android.view.InputEventReceiver +import android.view.InputEventSender +import android.view.KeyEvent +import android.view.MotionEvent +import java.util.concurrent.CountDownLatch +import org.junit.Assert.assertEquals +import org.junit.After +import org.junit.Before +import org.junit.Test + +private fun assertKeyEvent(expected: KeyEvent, received: KeyEvent) { + assertEquals(expected.action, received.action) + assertEquals(expected.deviceId, received.deviceId) + assertEquals(expected.downTime, received.downTime) + assertEquals(expected.eventTime, received.eventTime) + assertEquals(expected.keyCode, received.keyCode) + assertEquals(expected.scanCode, received.scanCode) + assertEquals(expected.repeatCount, received.repeatCount) + assertEquals(expected.metaState, received.metaState) + assertEquals(expected.flags, received.flags) + assertEquals(expected.source, received.source) + assertEquals(expected.displayId, received.displayId) +} + +class TestInputEventReceiver(channel: InputChannel, looper: Looper) : + InputEventReceiver(channel, looper) { + companion object { + const val TAG = "TestInputEventReceiver" + } + + var lastEvent: InputEvent? = null + + override fun onInputEvent(event: InputEvent) { + lastEvent = when (event) { + is KeyEvent -> KeyEvent.obtain(event) + is MotionEvent -> MotionEvent.obtain(event) + else -> throw Exception("Received $event is neither a key nor a motion") + } + finishInputEvent(event, true /*handled*/) + } +} + +class TestInputEventSender(channel: InputChannel, looper: Looper) : + InputEventSender(channel, looper) { + companion object { + const val TAG = "TestInputEventSender" + } + data class FinishedResult(val seq: Int, val handled: Boolean) + + private var mFinishedSignal = CountDownLatch(1) + override fun onInputEventFinished(seq: Int, handled: Boolean) { + finishedResult = FinishedResult(seq, handled) + mFinishedSignal.countDown() + } + lateinit var finishedResult: FinishedResult + + fun waitForFinish() { + mFinishedSignal.await() + mFinishedSignal = CountDownLatch(1) // Ready for next event + } +} + +class InputEventSenderAndReceiverTest { + companion object { + private const val TAG = "InputEventSenderAndReceiverTest" + } + private val mHandlerThread = HandlerThread("Process input events") + private lateinit var mReceiver: TestInputEventReceiver + private lateinit var mSender: TestInputEventSender + + @Before + fun setUp() { + val channels = InputChannel.openInputChannelPair("TestChannel") + mHandlerThread.start() + + val looper = mHandlerThread.getLooper() + mSender = TestInputEventSender(channels[0], looper) + mReceiver = TestInputEventReceiver(channels[1], looper) + } + + @After + fun tearDown() { + mHandlerThread.quitSafely() + } + + @Test + fun testSendAndReceiveKey() { + val key = KeyEvent(1 /*downTime*/, 1 /*eventTime*/, KeyEvent.ACTION_DOWN, + KeyEvent.KEYCODE_A, 0 /*repeat*/) + val seq = 10 + mSender.sendInputEvent(seq, key) + mSender.waitForFinish() + + // Check receiver + assertKeyEvent(key, mReceiver.lastEvent!! as KeyEvent) + + // Check sender + assertEquals(seq, mSender.finishedResult.seq) + assertEquals(true, mSender.finishedResult.handled) + } +} |