Skip to content

Commit 3bdcdd8

Browse files
author
Jeff Brown
committed
Be more careful about exceptions in input callbacks.
consumeEvents() may be called reentrantly so we need to be careful when handling exceptions. When called directly through JNI, the exception should be allowed to bubble up to the caller. When called from a Looper callback, the exception should be recorded on the MessageQueue and bubbled when the call to nativePollOnce() returns. Bug: 6312938 Change-Id: Ief5e315802f586aa85af7eef1bd6e9bea4ce24ab
1 parent 9f25b7f commit 3bdcdd8

File tree

1 file changed

+45
-40
lines changed

1 file changed

+45
-40
lines changed

core/jni/android_view_InputEventReceiver.cpp

Lines changed: 45 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,7 @@ class NativeInputEventReceiver : public RefBase {
5252

5353
status_t initialize();
5454
status_t finishInputEvent(uint32_t seq, bool handled);
55-
status_t consumeEvents(bool consumeBatches);
56-
static int handleReceiveCallback(int receiveFd, int events, void* data);
55+
status_t consumeEvents(JNIEnv* env, bool consumeBatches);
5756

5857
protected:
5958
virtual ~NativeInputEventReceiver();
@@ -68,6 +67,8 @@ class NativeInputEventReceiver : public RefBase {
6867
const char* getInputChannelName() {
6968
return mInputConsumer.getChannel()->getName().string();
7069
}
70+
71+
static int handleReceiveCallback(int receiveFd, int events, void* data);
7172
};
7273

7374

@@ -128,11 +129,13 @@ int NativeInputEventReceiver::handleReceiveCallback(int receiveFd, int events, v
128129
return 1;
129130
}
130131

131-
status_t status = r->consumeEvents(false /*consumeBatches*/);
132+
JNIEnv* env = AndroidRuntime::getJNIEnv();
133+
status_t status = r->consumeEvents(env, false /*consumeBatches*/);
134+
r->mMessageQueue->raiseAndClearException(env, "handleReceiveCallback");
132135
return status == OK || status == NO_MEMORY ? 1 : 0;
133136
}
134137

135-
status_t NativeInputEventReceiver::consumeEvents(bool consumeBatches) {
138+
status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env, bool consumeBatches) {
136139
#if DEBUG_DISPATCH_CYCLE
137140
ALOGD("channel '%s' ~ Consuming input events, consumeBatches=%s.", getInputChannelName(),
138141
consumeBatches ? "true" : "false");
@@ -142,15 +145,16 @@ status_t NativeInputEventReceiver::consumeEvents(bool consumeBatches) {
142145
mBatchedInputEventPending = false;
143146
}
144147

145-
JNIEnv* env = AndroidRuntime::getJNIEnv();
148+
bool skipCallbacks = false;
146149
for (;;) {
147150
uint32_t seq;
148151
InputEvent* inputEvent;
149152
status_t status = mInputConsumer.consume(&mInputEventFactory,
150153
consumeBatches, &seq, &inputEvent);
151154
if (status) {
152155
if (status == WOULD_BLOCK) {
153-
if (mInputConsumer.hasPendingBatch() && !mBatchedInputEventPending) {
156+
if (!skipCallbacks && !mBatchedInputEventPending
157+
&& mInputConsumer.hasPendingBatch()) {
154158
// There is a pending batch. Come back later.
155159
mBatchedInputEventPending = true;
156160
#if DEBUG_DISPATCH_CYCLE
@@ -159,8 +163,8 @@ status_t NativeInputEventReceiver::consumeEvents(bool consumeBatches) {
159163
#endif
160164
env->CallVoidMethod(mReceiverObjGlobal,
161165
gInputEventReceiverClassInfo.dispatchBatchedInputEventPending);
162-
if (mMessageQueue->raiseAndClearException(
163-
env, "dispatchBatchedInputEventPending")) {
166+
if (env->ExceptionCheck()) {
167+
ALOGE("Exception dispatching batched input events.");
164168
mBatchedInputEventPending = false; // try again later
165169
}
166170
}
@@ -172,46 +176,47 @@ status_t NativeInputEventReceiver::consumeEvents(bool consumeBatches) {
172176
}
173177
assert(inputEvent);
174178

175-
jobject inputEventObj;
176-
switch (inputEvent->getType()) {
177-
case AINPUT_EVENT_TYPE_KEY:
179+
if (!skipCallbacks) {
180+
jobject inputEventObj;
181+
switch (inputEvent->getType()) {
182+
case AINPUT_EVENT_TYPE_KEY:
178183
#if DEBUG_DISPATCH_CYCLE
179-
ALOGD("channel '%s' ~ Received key event.", getInputChannelName());
184+
ALOGD("channel '%s' ~ Received key event.", getInputChannelName());
180185
#endif
181-
inputEventObj = android_view_KeyEvent_fromNative(env,
182-
static_cast<KeyEvent*>(inputEvent));
183-
mMessageQueue->raiseAndClearException(env, "new KeyEvent");
184-
break;
186+
inputEventObj = android_view_KeyEvent_fromNative(env,
187+
static_cast<KeyEvent*>(inputEvent));
188+
break;
185189

186-
case AINPUT_EVENT_TYPE_MOTION:
190+
case AINPUT_EVENT_TYPE_MOTION:
187191
#if DEBUG_DISPATCH_CYCLE
188-
ALOGD("channel '%s' ~ Received motion event.", getInputChannelName());
192+
ALOGD("channel '%s' ~ Received motion event.", getInputChannelName());
189193
#endif
190-
inputEventObj = android_view_MotionEvent_obtainAsCopy(env,
191-
static_cast<MotionEvent*>(inputEvent));
192-
mMessageQueue->raiseAndClearException(env, "new MotionEvent");
193-
break;
194-
195-
default:
196-
assert(false); // InputConsumer should prevent this from ever happening
197-
inputEventObj = NULL;
198-
}
194+
inputEventObj = android_view_MotionEvent_obtainAsCopy(env,
195+
static_cast<MotionEvent*>(inputEvent));
196+
break;
199197

200-
if (!inputEventObj) {
201-
ALOGW("channel '%s' ~ Failed to obtain event object.", getInputChannelName());
202-
mInputConsumer.sendFinishedSignal(seq, false);
203-
continue;
204-
}
198+
default:
199+
assert(false); // InputConsumer should prevent this from ever happening
200+
inputEventObj = NULL;
201+
}
205202

203+
if (inputEventObj) {
206204
#if DEBUG_DISPATCH_CYCLE
207-
ALOGD("channel '%s' ~ Dispatching input event.", getInputChannelName());
205+
ALOGD("channel '%s' ~ Dispatching input event.", getInputChannelName());
208206
#endif
209-
env->CallVoidMethod(mReceiverObjGlobal,
210-
gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);
211-
212-
env->DeleteLocalRef(inputEventObj);
207+
env->CallVoidMethod(mReceiverObjGlobal,
208+
gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);
209+
if (env->ExceptionCheck()) {
210+
ALOGE("Exception dispatching input event.");
211+
skipCallbacks = true;
212+
}
213+
} else {
214+
ALOGW("channel '%s' ~ Failed to obtain event object.", getInputChannelName());
215+
skipCallbacks = true;
216+
}
217+
}
213218

214-
if (mMessageQueue->raiseAndClearException(env, "dispatchInputEvent")) {
219+
if (skipCallbacks) {
215220
mInputConsumer.sendFinishedSignal(seq, false);
216221
}
217222
}
@@ -268,8 +273,8 @@ static void nativeFinishInputEvent(JNIEnv* env, jclass clazz, jint receiverPtr,
268273
static void nativeConsumeBatchedInputEvents(JNIEnv* env, jclass clazz, jint receiverPtr) {
269274
sp<NativeInputEventReceiver> receiver =
270275
reinterpret_cast<NativeInputEventReceiver*>(receiverPtr);
271-
status_t status = receiver->consumeEvents(true /*consumeBatches*/);
272-
if (status && status != DEAD_OBJECT) {
276+
status_t status = receiver->consumeEvents(env, true /*consumeBatches*/);
277+
if (status && status != DEAD_OBJECT && !env->ExceptionCheck()) {
273278
String8 message;
274279
message.appendFormat("Failed to consume batched input event. status=%d", status);
275280
jniThrowRuntimeException(env, message.string());

0 commit comments

Comments
 (0)