1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.test.input 18 19 import android.os.HandlerThread 20 import android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS 21 import android.os.Looper 22 import android.view.InputChannel 23 import android.view.InputEvent 24 import android.view.InputEventReceiver 25 import android.view.InputEventSender 26 import android.view.KeyEvent 27 import android.view.MotionEvent 28 import java.util.concurrent.LinkedBlockingQueue 29 import java.util.concurrent.TimeUnit 30 import org.junit.Assert.assertEquals 31 import org.junit.After 32 import org.junit.Before 33 import org.junit.Test 34 35 private fun assertKeyEvent(expected: KeyEvent, received: KeyEvent) { 36 assertEquals(expected.action, received.action) 37 assertEquals(expected.deviceId, received.deviceId) 38 assertEquals(expected.downTime, received.downTime) 39 assertEquals(expected.eventTime, received.eventTime) 40 assertEquals(expected.keyCode, received.keyCode) 41 assertEquals(expected.scanCode, received.scanCode) 42 assertEquals(expected.repeatCount, received.repeatCount) 43 assertEquals(expected.metaState, received.metaState) 44 assertEquals(expected.flags, received.flags) 45 assertEquals(expected.source, received.source) 46 assertEquals(expected.displayId, received.displayId) 47 } 48 49 private fun <T> getEvent(queue: LinkedBlockingQueue<T>): T { 50 try { 51 return queue.poll(DEFAULT_DISPATCHING_TIMEOUT_MILLIS.toLong(), TimeUnit.MILLISECONDS) 52 } catch (e: InterruptedException) { 53 throw RuntimeException("Unexpectedly interrupted while waiting for event") 54 } 55 } 56 57 class TestInputEventReceiver(channel: InputChannel, looper: Looper) : 58 InputEventReceiver(channel, looper) { 59 private val mInputEvents = LinkedBlockingQueue<InputEvent>() 60 61 override fun onInputEvent(event: InputEvent) { 62 when (event) { 63 is KeyEvent -> mInputEvents.put(KeyEvent.obtain(event)) 64 is MotionEvent -> mInputEvents.put(MotionEvent.obtain(event)) 65 else -> throw Exception("Received $event is neither a key nor a motion") 66 } 67 finishInputEvent(event, true /*handled*/) 68 } 69 70 fun getInputEvent(): InputEvent { 71 return getEvent(mInputEvents) 72 } 73 } 74 75 class TestInputEventSender(channel: InputChannel, looper: Looper) : 76 InputEventSender(channel, looper) { 77 data class FinishedSignal(val seq: Int, val handled: Boolean) 78 data class Timeline(val inputEventId: Int, val gpuCompletedTime: Long, val presentTime: Long) 79 80 private val mFinishedSignals = LinkedBlockingQueue<FinishedSignal>() 81 private val mTimelines = LinkedBlockingQueue<Timeline>() 82 83 override fun onInputEventFinished(seq: Int, handled: Boolean) { 84 mFinishedSignals.put(FinishedSignal(seq, handled)) 85 } 86 87 override fun onTimelineReported(inputEventId: Int, gpuCompletedTime: Long, presentTime: Long) { 88 mTimelines.put(Timeline(inputEventId, gpuCompletedTime, presentTime)) 89 } 90 91 fun getFinishedSignal(): FinishedSignal { 92 return getEvent(mFinishedSignals) 93 } 94 95 fun getTimeline(): Timeline { 96 return getEvent(mTimelines) 97 } 98 } 99 100 class InputEventSenderAndReceiverTest { 101 companion object { 102 private const val TAG = "InputEventSenderAndReceiverTest" 103 } 104 private val mHandlerThread = HandlerThread("Process input events") 105 private lateinit var mReceiver: TestInputEventReceiver 106 private lateinit var mSender: TestInputEventSender 107 108 @Before 109 fun setUp() { 110 val channels = InputChannel.openInputChannelPair("TestChannel") 111 mHandlerThread.start() 112 113 val looper = mHandlerThread.getLooper() 114 mSender = TestInputEventSender(channels[0], looper) 115 mReceiver = TestInputEventReceiver(channels[1], looper) 116 } 117 118 @After 119 fun tearDown() { 120 mHandlerThread.quitSafely() 121 } 122 123 @Test 124 fun testSendAndReceiveKey() { 125 val key = KeyEvent(1 /*downTime*/, 1 /*eventTime*/, KeyEvent.ACTION_DOWN, 126 KeyEvent.KEYCODE_A, 0 /*repeat*/) 127 val seq = 10 128 mSender.sendInputEvent(seq, key) 129 val receivedKey = mReceiver.getInputEvent() as KeyEvent 130 val finishedSignal = mSender.getFinishedSignal() 131 132 // Check receiver 133 assertKeyEvent(key, receivedKey) 134 135 // Check sender 136 assertEquals(TestInputEventSender.FinishedSignal(seq, handled = true), finishedSignal) 137 } 138 139 // The timeline case is slightly unusual because it goes from InputConsumer to InputPublisher. 140 @Test 141 fun testSendAndReceiveTimeline() { 142 val sent = TestInputEventSender.Timeline( 143 inputEventId = 1, gpuCompletedTime = 2, presentTime = 3) 144 mReceiver.reportTimeline(sent.inputEventId, sent.gpuCompletedTime, sent.presentTime) 145 val received = mSender.getTimeline() 146 assertEquals(sent, received) 147 } 148 149 // If an invalid timeline is sent, the channel should get closed. This helps surface any 150 // app-originating bugs early, and forces the work-around to happen in the early stages of the 151 // event processing. 152 @Test 153 fun testSendAndReceiveInvalidTimeline() { 154 val sent = TestInputEventSender.Timeline( 155 inputEventId = 1, gpuCompletedTime = 3, presentTime = 2) 156 mReceiver.reportTimeline(sent.inputEventId, sent.gpuCompletedTime, sent.presentTime) 157 val received = mSender.getTimeline() 158 assertEquals(null, received) 159 // Sender will no longer receive callbacks for this fd, even if receiver sends a valid 160 // timeline later 161 mReceiver.reportTimeline(2 /*inputEventId*/, 3 /*gpuCompletedTime*/, 4 /*presentTime*/) 162 val receivedSecondTimeline = mSender.getTimeline() 163 assertEquals(null, receivedSecondTimeline) 164 } 165 } 166