| /* |
| * 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.view.InputChannel |
| import android.view.InputDevice |
| import android.view.MotionEvent |
| import android.view.WindowManagerPolicyConstants.PointerEventListener |
| |
| import com.android.server.UiThread |
| import com.android.server.wm.PointerEventDispatcher |
| import org.junit.Assert.assertEquals |
| import org.junit.Assert.assertNull |
| import org.junit.After |
| import org.junit.Before |
| import org.junit.Test |
| |
| private class CrashingPointerEventListener : PointerEventListener { |
| override fun onPointerEvent(motionEvent: MotionEvent) { |
| throw IllegalArgumentException("This listener crashes when input event occurs") |
| } |
| } |
| |
| class PointerEventDispatcherTest { |
| companion object { |
| private const val TAG = "PointerEventDispatcherTest" |
| } |
| private val mHandlerThread = HandlerThread("Process input events") |
| private lateinit var mSender: SpyInputEventSender |
| private lateinit var mPointerEventDispatcher: PointerEventDispatcher |
| private val mListener = CrashingPointerEventListener() |
| |
| @Before |
| fun setUp() { |
| val channels = InputChannel.openInputChannelPair("TestChannel") |
| |
| mHandlerThread.start() |
| val looper = mHandlerThread.getLooper() |
| mSender = SpyInputEventSender(channels[0], looper) |
| |
| mPointerEventDispatcher = PointerEventDispatcher(channels[1]) |
| mPointerEventDispatcher.registerInputEventListener(mListener) |
| } |
| |
| @After |
| fun tearDown() { |
| mHandlerThread.quitSafely() |
| } |
| |
| @Test |
| fun testSendMotionToCrashingListenerDoesNotCrash() { |
| // The exception will occur on the UiThread, so we can't catch it here on the test thread |
| UiThread.get().setUncaughtExceptionHandler { thread, exception -> |
| if (thread == UiThread.get() && exception is IllegalArgumentException) { |
| // do nothing - this is the exception that we need to ignore |
| } else { |
| throw exception |
| } |
| } |
| |
| // The MotionEvent properties aren't important for this test, as long as the event |
| // is a pointer event, so that it gets processed by CrashingPointerEventListener |
| val downTime = 0L |
| val motionEvent = MotionEvent.obtain(downTime, downTime, |
| MotionEvent.ACTION_DOWN, 0f /* x */, 0f /* y */, 0 /* metaState */) |
| motionEvent.source = InputDevice.SOURCE_TOUCHSCREEN |
| val seq = 10 |
| mSender.sendInputEvent(seq, motionEvent) |
| val finishedSignal = mSender.getFinishedSignal() |
| |
| // Since the listener raises an exception during the event handling, the event should be |
| // marked as 'not handled'. |
| assertEquals(SpyInputEventSender.FinishedSignal(seq, handled = false), finishedSignal) |
| // Ensure that there aren't double finish calls. This would crash if there's a call |
| // to finish twice. |
| assertNull(mSender.getFinishedSignal()) |
| } |
| } |