Delete try-finally beacuse calling finishInputEvent twice will cause 'Native Crash'

If there is an exception, finishInputEvent method will be called, then NativeInputEventReceiver also send finish signal,will cause a native crash,'Abort message: 'Could not find consume time for seq=xxxx'

[1] https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/jni/android_view_InputEventReceiver.cpp;l=441?q=InputEventRe&ss=android%2Fplatform%2Fsuperproject:frameworks%2F
[2] https://cs.android.com/android/platform/superproject/+/master:frameworks/native/libs/input/InputTransport.cpp;l=1259?q=InputTRAN&ss=android%2Fplatform%2Fsuperproject:frameworks%2F

Bug: 169866723
Test: atest InputEventSenderAndReceiverTest
Signed-off-by: chenxinyu <[email protected]>
Change-Id: Ib834e2a960741f7fa33a0661c67f305af0db517a
diff --git a/tests/Input/Android.bp b/tests/Input/Android.bp
index eacf5b2..6e65350 100644
--- a/tests/Input/Android.bp
+++ b/tests/Input/Android.bp
@@ -18,6 +18,7 @@
     static_libs: [
         "androidx.test.ext.junit",
         "androidx.test.rules",
+        "services.core.unboosted",
         "truth-prebuilt",
         "ub-uiautomator",
     ],
diff --git a/tests/Input/src/com/android/test/input/InputEventSenderAndReceiverTest.kt b/tests/Input/src/com/android/test/input/InputEventSenderAndReceiverTest.kt
index 014efc2..37b67f4 100644
--- a/tests/Input/src/com/android/test/input/InputEventSenderAndReceiverTest.kt
+++ b/tests/Input/src/com/android/test/input/InputEventSenderAndReceiverTest.kt
@@ -17,16 +17,11 @@
 package com.android.test.input
 
 import android.os.HandlerThread
-import android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS
 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.LinkedBlockingQueue
-import java.util.concurrent.TimeUnit
 import org.junit.Assert.assertEquals
 import org.junit.After
 import org.junit.Before
@@ -46,54 +41,19 @@
     assertEquals(expected.displayId, received.displayId)
 }
 
-private fun <T> getEvent(queue: LinkedBlockingQueue<T>): T {
-    try {
-        return queue.poll(DEFAULT_DISPATCHING_TIMEOUT_MILLIS.toLong(), TimeUnit.MILLISECONDS)
-    } catch (e: InterruptedException) {
-        throw RuntimeException("Unexpectedly interrupted while waiting for event")
-    }
+private fun getTestKeyEvent(): KeyEvent {
+    return KeyEvent(1 /*downTime*/, 1 /*eventTime*/, KeyEvent.ACTION_DOWN,
+                KeyEvent.KEYCODE_A, 0 /*repeat*/)
 }
 
-class TestInputEventReceiver(channel: InputChannel, looper: Looper) :
+private class CrashingInputEventReceiver(channel: InputChannel, looper: Looper) :
         InputEventReceiver(channel, looper) {
-    private val mInputEvents = LinkedBlockingQueue<InputEvent>()
-
     override fun onInputEvent(event: InputEvent) {
-        when (event) {
-            is KeyEvent -> mInputEvents.put(KeyEvent.obtain(event))
-            is MotionEvent -> mInputEvents.put(MotionEvent.obtain(event))
-            else -> throw Exception("Received $event is neither a key nor a motion")
+        try {
+            throw IllegalArgumentException("This receiver crashes when it receives input event")
+        } finally {
+            finishInputEvent(event, true /*handled*/)
         }
-        finishInputEvent(event, true /*handled*/)
-    }
-
-    fun getInputEvent(): InputEvent {
-        return getEvent(mInputEvents)
-    }
-}
-
-class TestInputEventSender(channel: InputChannel, looper: Looper) :
-        InputEventSender(channel, looper) {
-    data class FinishedSignal(val seq: Int, val handled: Boolean)
-    data class Timeline(val inputEventId: Int, val gpuCompletedTime: Long, val presentTime: Long)
-
-    private val mFinishedSignals = LinkedBlockingQueue<FinishedSignal>()
-    private val mTimelines = LinkedBlockingQueue<Timeline>()
-
-    override fun onInputEventFinished(seq: Int, handled: Boolean) {
-        mFinishedSignals.put(FinishedSignal(seq, handled))
-    }
-
-    override fun onTimelineReported(inputEventId: Int, gpuCompletedTime: Long, presentTime: Long) {
-        mTimelines.put(Timeline(inputEventId, gpuCompletedTime, presentTime))
-    }
-
-    fun getFinishedSignal(): FinishedSignal {
-        return getEvent(mFinishedSignals)
-    }
-
-    fun getTimeline(): Timeline {
-        return getEvent(mTimelines)
     }
 }
 
@@ -102,8 +62,8 @@
         private const val TAG = "InputEventSenderAndReceiverTest"
     }
     private val mHandlerThread = HandlerThread("Process input events")
-    private lateinit var mReceiver: TestInputEventReceiver
-    private lateinit var mSender: TestInputEventSender
+    private lateinit var mReceiver: SpyInputEventReceiver
+    private lateinit var mSender: SpyInputEventSender
 
     @Before
     fun setUp() {
@@ -111,8 +71,8 @@
         mHandlerThread.start()
 
         val looper = mHandlerThread.getLooper()
-        mSender = TestInputEventSender(channels[0], looper)
-        mReceiver = TestInputEventReceiver(channels[1], looper)
+        mSender = SpyInputEventSender(channels[0], looper)
+        mReceiver = SpyInputEventReceiver(channels[1], looper)
     }
 
     @After
@@ -122,8 +82,7 @@
 
     @Test
     fun testSendAndReceiveKey() {
-        val key = KeyEvent(1 /*downTime*/, 1 /*eventTime*/, KeyEvent.ACTION_DOWN,
-                KeyEvent.KEYCODE_A, 0 /*repeat*/)
+        val key = getTestKeyEvent()
         val seq = 10
         mSender.sendInputEvent(seq, key)
         val receivedKey = mReceiver.getInputEvent() as KeyEvent
@@ -133,13 +92,13 @@
         assertKeyEvent(key, receivedKey)
 
         // Check sender
-        assertEquals(TestInputEventSender.FinishedSignal(seq, handled = true), finishedSignal)
+        assertEquals(SpyInputEventSender.FinishedSignal(seq, handled = true), finishedSignal)
     }
 
     // The timeline case is slightly unusual because it goes from InputConsumer to InputPublisher.
     @Test
     fun testSendAndReceiveTimeline() {
-        val sent = TestInputEventSender.Timeline(
+        val sent = SpyInputEventSender.Timeline(
             inputEventId = 1, gpuCompletedTime = 2, presentTime = 3)
         mReceiver.reportTimeline(sent.inputEventId, sent.gpuCompletedTime, sent.presentTime)
         val received = mSender.getTimeline()
@@ -151,7 +110,7 @@
     // event processing.
     @Test
     fun testSendAndReceiveInvalidTimeline() {
-        val sent = TestInputEventSender.Timeline(
+        val sent = SpyInputEventSender.Timeline(
             inputEventId = 1, gpuCompletedTime = 3, presentTime = 2)
         mReceiver.reportTimeline(sent.inputEventId, sent.gpuCompletedTime, sent.presentTime)
         val received = mSender.getTimeline()
@@ -162,4 +121,41 @@
         val receivedSecondTimeline = mSender.getTimeline()
         assertEquals(null, receivedSecondTimeline)
     }
+
+    /**
+     * If a receiver throws an exception during 'onInputEvent' execution, the 'finally' block still
+     * completes, and therefore, finishInputEvent is called. Make sure that there's no crash in the
+     * native layer in these circumstances.
+     * In this test, we are reusing the 'mHandlerThread', but we are creating new sender and
+     * receiver.
+     */
+    @Test
+    fun testCrashingReceiverDoesNotCrash() {
+        val channels = InputChannel.openInputChannelPair("TestChannel2")
+        val sender = SpyInputEventSender(channels[0], mHandlerThread.getLooper())
+
+        // Need a separate thread for the receiver so that the sender can still get the response
+        // after the receiver crashes
+        val receiverThread = HandlerThread("Receive input events")
+        receiverThread.start()
+        val crashingReceiver = CrashingInputEventReceiver(channels[1], receiverThread.getLooper())
+        receiverThread.setUncaughtExceptionHandler { thread, exception ->
+            if (thread == receiverThread && exception is IllegalArgumentException) {
+                // do nothing - this is the exception that we need to ignore
+            } else {
+                throw exception
+            }
+        }
+
+        val key = getTestKeyEvent()
+        val seq = 11
+        sender.sendInputEvent(seq, key)
+        val finishedSignal = sender.getFinishedSignal()
+        assertEquals(SpyInputEventSender.FinishedSignal(seq, handled = true), finishedSignal)
+
+        // Clean up
+        crashingReceiver.dispose()
+        sender.dispose()
+        receiverThread.quitSafely()
+    }
 }
diff --git a/tests/Input/src/com/android/test/input/PointerEventDispatcherTest.kt b/tests/Input/src/com/android/test/input/PointerEventDispatcherTest.kt
new file mode 100644
index 0000000..1099878
--- /dev/null
+++ b/tests/Input/src/com/android/test/input/PointerEventDispatcherTest.kt
@@ -0,0 +1,93 @@
+/*
+ * 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())
+    }
+}
diff --git a/tests/Input/src/com/android/test/input/SpyInputEventSenderAndReceiver.kt b/tests/Input/src/com/android/test/input/SpyInputEventSenderAndReceiver.kt
new file mode 100644
index 0000000..2d9af9a
--- /dev/null
+++ b/tests/Input/src/com/android/test/input/SpyInputEventSenderAndReceiver.kt
@@ -0,0 +1,75 @@
+/*
+ * 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.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS
+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.LinkedBlockingQueue
+import java.util.concurrent.TimeUnit
+
+private fun <T> getEvent(queue: LinkedBlockingQueue<T>): T? {
+    return queue.poll(DEFAULT_DISPATCHING_TIMEOUT_MILLIS.toLong(), TimeUnit.MILLISECONDS)
+}
+
+class SpyInputEventReceiver(channel: InputChannel, looper: Looper) :
+        InputEventReceiver(channel, looper) {
+    private val mInputEvents = LinkedBlockingQueue<InputEvent>()
+
+    override fun onInputEvent(event: InputEvent) {
+        when (event) {
+            is KeyEvent -> mInputEvents.put(KeyEvent.obtain(event))
+            is MotionEvent -> mInputEvents.put(MotionEvent.obtain(event))
+            else -> throw Exception("Received $event is neither a key nor a motion")
+        }
+        finishInputEvent(event, true /*handled*/)
+    }
+
+    fun getInputEvent(): InputEvent? {
+        return getEvent(mInputEvents)
+    }
+}
+
+class SpyInputEventSender(channel: InputChannel, looper: Looper) :
+        InputEventSender(channel, looper) {
+    data class FinishedSignal(val seq: Int, val handled: Boolean)
+    data class Timeline(val inputEventId: Int, val gpuCompletedTime: Long, val presentTime: Long)
+
+    private val mFinishedSignals = LinkedBlockingQueue<FinishedSignal>()
+    private val mTimelines = LinkedBlockingQueue<Timeline>()
+
+    override fun onInputEventFinished(seq: Int, handled: Boolean) {
+        mFinishedSignals.put(FinishedSignal(seq, handled))
+    }
+
+    override fun onTimelineReported(inputEventId: Int, gpuCompletedTime: Long, presentTime: Long) {
+        mTimelines.put(Timeline(inputEventId, gpuCompletedTime, presentTime))
+    }
+
+    fun getFinishedSignal(): FinishedSignal? {
+        return getEvent(mFinishedSignals)
+    }
+
+    fun getTimeline(): Timeline? {
+        return getEvent(mTimelines)
+    }
+}