Merge "Move 'notifyFinishedSignal' into a separate function" into sc-dev

This commit is contained in:
TreeHugger Robot
2021-03-23 11:18:33 +00:00
committed by Android (Google) Code Review
2 changed files with 88 additions and 66 deletions

View File

@@ -18,28 +18,28 @@
//#define LOG_NDEBUG 0
#include <nativehelper/JNIHelp.h>
#include <android_runtime/AndroidRuntime.h>
#include <log/log.h>
#include <utils/Looper.h>
#include <input/InputTransport.h>
#include <log/log.h>
#include <nativehelper/JNIHelp.h>
#include <nativehelper/ScopedLocalRef.h>
#include <utils/Looper.h>
#include "android_os_MessageQueue.h"
#include "android_view_InputChannel.h"
#include "android_view_KeyEvent.h"
#include "android_view_MotionEvent.h"
#include "core_jni_helpers.h"
#include <nativehelper/ScopedLocalRef.h>
#include <inttypes.h>
#include <unordered_map>
#include "core_jni_helpers.h"
using android::base::Result;
namespace android {
// Log debug messages about the dispatch cycle.
static const bool kDebugDispatchCycle = false;
static constexpr bool kDebugDispatchCycle = false;
static struct {
jclass clazz;
@@ -74,8 +74,10 @@ private:
return mInputPublisher.getChannel()->getName();
}
virtual int handleEvent(int receiveFd, int events, void* data);
int handleEvent(int receiveFd, int events, void* data) override;
status_t receiveFinishedSignals(JNIEnv* env);
bool notifyFinishedSignal(JNIEnv* env, jobject sender, const InputPublisher::Finished& finished,
bool skipCallbacks);
};
NativeInputEventSender::NativeInputEventSender(JNIEnv* env, jobject senderWeak,
@@ -196,8 +198,13 @@ status_t NativeInputEventSender::receiveFinishedSignals(JNIEnv* env) {
ALOGD("channel '%s' ~ Receiving finished signals.", getInputChannelName().c_str());
}
ScopedLocalRef<jobject> senderObj(env, NULL);
bool skipCallbacks = false;
ScopedLocalRef<jobject> senderObj(env, jniGetReferent(env, mSenderWeakGlobal));
if (!senderObj.get()) {
ALOGW("channel '%s' ~ Sender object was finalized without being disposed.",
getInputChannelName().c_str());
return DEAD_OBJECT;
}
bool skipCallbacks = false; // stop calling Java functions after an exception occurs
for (;;) {
Result<InputPublisher::Finished> result = mInputPublisher.receiveFinishedSignal();
if (!result.ok()) {
@@ -206,45 +213,55 @@ status_t NativeInputEventSender::receiveFinishedSignals(JNIEnv* env) {
return OK;
}
ALOGE("channel '%s' ~ Failed to consume finished signals. status=%d",
getInputChannelName().c_str(), status);
getInputChannelName().c_str(), status);
return status;
}
auto it = mPublishedSeqMap.find(result->seq);
if (it == mPublishedSeqMap.end()) {
continue;
}
uint32_t seq = it->second;
mPublishedSeqMap.erase(it);
if (kDebugDispatchCycle) {
ALOGD("channel '%s' ~ Received finished signal, seq=%u, handled=%s, pendingEvents=%zu.",
getInputChannelName().c_str(), seq, result->handled ? "true" : "false",
mPublishedSeqMap.size());
}
if (!skipCallbacks) {
if (!senderObj.get()) {
senderObj.reset(jniGetReferent(env, mSenderWeakGlobal));
if (!senderObj.get()) {
ALOGW("channel '%s' ~ Sender object was finalized without being disposed.",
getInputChannelName().c_str());
return DEAD_OBJECT;
}
}
env->CallVoidMethod(senderObj.get(),
gInputEventSenderClassInfo.dispatchInputEventFinished,
static_cast<jint>(seq), static_cast<jboolean>(result->handled));
if (env->ExceptionCheck()) {
ALOGE("Exception dispatching finished signal.");
skipCallbacks = true;
}
const bool notified = notifyFinishedSignal(env, senderObj.get(), *result, skipCallbacks);
if (!notified) {
skipCallbacks = true;
}
}
}
/**
* Invoke the Java function dispatchInputEventFinished for the received "Finished" signal.
* Set the variable 'skipCallbacks' to 'true' if a Java exception occurred.
* Java function will only be called if 'skipCallbacks' is originally 'false'.
*
* Return "false" if an exception occurred while calling the Java function
* "true" otherwise
*/
bool NativeInputEventSender::notifyFinishedSignal(JNIEnv* env, jobject sender,
const InputPublisher::Finished& finished,
bool skipCallbacks) {
auto it = mPublishedSeqMap.find(finished.seq);
if (it == mPublishedSeqMap.end()) {
ALOGW("Received 'finished' signal for unknown seq number = %" PRIu32, finished.seq);
// Since this is coming from the receiver (typically app), it's possible that an app
// does something wrong and sends bad data. Just ignore and process other events.
return true;
}
const uint32_t seq = it->second;
mPublishedSeqMap.erase(it);
if (kDebugDispatchCycle) {
ALOGD("channel '%s' ~ Received finished signal, seq=%u, handled=%s, pendingEvents=%zu.",
getInputChannelName().c_str(), seq, finished.handled ? "true" : "false",
mPublishedSeqMap.size());
}
if (skipCallbacks) {
return true;
}
env->CallVoidMethod(sender, gInputEventSenderClassInfo.dispatchInputEventFinished,
static_cast<jint>(seq), static_cast<jboolean>(finished.handled));
if (env->ExceptionCheck()) {
ALOGE("Exception dispatching finished signal for seq=%" PRIu32, seq);
return false;
}
return true;
}
static jlong nativeInit(JNIEnv* env, jclass clazz, jobject senderWeak,
jobject inputChannelObj, jobject messageQueueObj) {

View File

@@ -17,6 +17,7 @@
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
@@ -24,7 +25,8 @@ import android.view.InputEventReceiver
import android.view.InputEventSender
import android.view.KeyEvent
import android.view.MotionEvent
import java.util.concurrent.CountDownLatch
import java.util.concurrent.LinkedBlockingQueue
import java.util.concurrent.TimeUnit
import org.junit.Assert.assertEquals
import org.junit.After
import org.junit.Before
@@ -44,41 +46,44 @@ private fun assertKeyEvent(expected: KeyEvent, received: KeyEvent) {
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")
}
}
class TestInputEventReceiver(channel: InputChannel, looper: Looper) :
InputEventReceiver(channel, looper) {
companion object {
const val TAG = "TestInputEventReceiver"
}
var lastEvent: InputEvent? = null
private val mInputEvents = LinkedBlockingQueue<InputEvent>()
override fun onInputEvent(event: InputEvent) {
lastEvent = when (event) {
is KeyEvent -> KeyEvent.obtain(event)
is MotionEvent -> MotionEvent.obtain(event)
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 TestInputEventSender(channel: InputChannel, looper: Looper) :
InputEventSender(channel, looper) {
companion object {
const val TAG = "TestInputEventSender"
}
data class FinishedResult(val seq: Int, val handled: Boolean)
data class FinishedSignal(val seq: Int, val handled: Boolean)
private val mFinishedSignals = LinkedBlockingQueue<FinishedSignal>()
private var mFinishedSignal = CountDownLatch(1)
override fun onInputEventFinished(seq: Int, handled: Boolean) {
finishedResult = FinishedResult(seq, handled)
mFinishedSignal.countDown()
mFinishedSignals.put(FinishedSignal(seq, handled))
}
lateinit var finishedResult: FinishedResult
fun waitForFinish() {
mFinishedSignal.await()
mFinishedSignal = CountDownLatch(1) // Ready for next event
fun getFinishedSignal(): FinishedSignal {
return getEvent(mFinishedSignals)
}
}
@@ -111,13 +116,13 @@ class InputEventSenderAndReceiverTest {
KeyEvent.KEYCODE_A, 0 /*repeat*/)
val seq = 10
mSender.sendInputEvent(seq, key)
mSender.waitForFinish()
val receivedKey = mReceiver.getInputEvent() as KeyEvent
val finishedSignal = mSender.getFinishedSignal()
// Check receiver
assertKeyEvent(key, mReceiver.lastEvent!! as KeyEvent)
assertKeyEvent(key, receivedKey)
// Check sender
assertEquals(seq, mSender.finishedResult.seq)
assertEquals(true, mSender.finishedResult.handled)
assertEquals(TestInputEventSender.FinishedSignal(seq, handled = true), finishedSignal)
}
}