Merge "Move 'notifyFinishedSignal' into a separate function" into sc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
49148d762d
@@ -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) {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user