Loading core/jni/android_view_InputEventSender.cpp +11 −16 Original line number Diff line number Diff line Loading @@ -34,6 +34,8 @@ #include "core_jni_helpers.h" using android::base::Result; namespace android { // Log debug messages about the dispatch cycle. Loading Loading @@ -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; } Loading @@ -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; } Loading @@ -226,7 +221,7 @@ 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", getInputChannelName().c_str(), seq, result->handled ? "true" : "false", mPublishedSeqMap.size()); } Loading @@ -242,7 +237,7 @@ status_t NativeInputEventSender::receiveFinishedSignals(JNIEnv* env) { env->CallVoidMethod(senderObj.get(), gInputEventSenderClassInfo.dispatchInputEventFinished, jint(seq), jboolean(handled)); static_cast<jint>(seq), static_cast<jboolean>(result->handled)); if (env->ExceptionCheck()) { ALOGE("Exception dispatching finished signal."); skipCallbacks = true; Loading tests/Input/src/com/android/test/input/InputEventAssignerTest.kt +1 −1 Original line number Diff line number Diff line Loading @@ -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) Loading tests/Input/src/com/android/test/input/InputEventSenderAndReceiverTest.kt 0 → 100644 +123 −0 Original line number Diff line number Diff line /* * 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) } } Loading
core/jni/android_view_InputEventSender.cpp +11 −16 Original line number Diff line number Diff line Loading @@ -34,6 +34,8 @@ #include "core_jni_helpers.h" using android::base::Result; namespace android { // Log debug messages about the dispatch cycle. Loading Loading @@ -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; } Loading @@ -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; } Loading @@ -226,7 +221,7 @@ 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", getInputChannelName().c_str(), seq, result->handled ? "true" : "false", mPublishedSeqMap.size()); } Loading @@ -242,7 +237,7 @@ status_t NativeInputEventSender::receiveFinishedSignals(JNIEnv* env) { env->CallVoidMethod(senderObj.get(), gInputEventSenderClassInfo.dispatchInputEventFinished, jint(seq), jboolean(handled)); static_cast<jint>(seq), static_cast<jboolean>(result->handled)); if (env->ExceptionCheck()) { ALOGE("Exception dispatching finished signal."); skipCallbacks = true; Loading
tests/Input/src/com/android/test/input/InputEventAssignerTest.kt +1 −1 Original line number Diff line number Diff line Loading @@ -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) Loading
tests/Input/src/com/android/test/input/InputEventSenderAndReceiverTest.kt 0 → 100644 +123 −0 Original line number Diff line number Diff line /* * 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) } }