Merge "Be more careful about exceptions in input callbacks."
diff --git a/core/jni/android_view_InputEventReceiver.cpp b/core/jni/android_view_InputEventReceiver.cpp
index 348437d..8f6f5f4 100644
--- a/core/jni/android_view_InputEventReceiver.cpp
+++ b/core/jni/android_view_InputEventReceiver.cpp
@@ -52,8 +52,7 @@
 
     status_t initialize();
     status_t finishInputEvent(uint32_t seq, bool handled);
-    status_t consumeEvents(bool consumeBatches);
-    static int handleReceiveCallback(int receiveFd, int events, void* data);
+    status_t consumeEvents(JNIEnv* env, bool consumeBatches);
 
 protected:
     virtual ~NativeInputEventReceiver();
@@ -68,6 +67,8 @@
     const char* getInputChannelName() {
         return mInputConsumer.getChannel()->getName().string();
     }
+
+    static int handleReceiveCallback(int receiveFd, int events, void* data);
 };
 
 
@@ -128,11 +129,13 @@
         return 1;
     }
 
-    status_t status = r->consumeEvents(false /*consumeBatches*/);
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    status_t status = r->consumeEvents(env, false /*consumeBatches*/);
+    r->mMessageQueue->raiseAndClearException(env, "handleReceiveCallback");
     return status == OK || status == NO_MEMORY ? 1 : 0;
 }
 
-status_t NativeInputEventReceiver::consumeEvents(bool consumeBatches) {
+status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env, bool consumeBatches) {
 #if DEBUG_DISPATCH_CYCLE
     ALOGD("channel '%s' ~ Consuming input events, consumeBatches=%s.", getInputChannelName(),
             consumeBatches ? "true" : "false");
@@ -142,7 +145,7 @@
         mBatchedInputEventPending = false;
     }
 
-    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    bool skipCallbacks = false;
     for (;;) {
         uint32_t seq;
         InputEvent* inputEvent;
@@ -150,7 +153,8 @@
                 consumeBatches, &seq, &inputEvent);
         if (status) {
             if (status == WOULD_BLOCK) {
-                if (mInputConsumer.hasPendingBatch() && !mBatchedInputEventPending) {
+                if (!skipCallbacks && !mBatchedInputEventPending
+                        && mInputConsumer.hasPendingBatch()) {
                     // There is a pending batch.  Come back later.
                     mBatchedInputEventPending = true;
 #if DEBUG_DISPATCH_CYCLE
@@ -159,8 +163,8 @@
 #endif
                     env->CallVoidMethod(mReceiverObjGlobal,
                             gInputEventReceiverClassInfo.dispatchBatchedInputEventPending);
-                    if (mMessageQueue->raiseAndClearException(
-                            env, "dispatchBatchedInputEventPending")) {
+                    if (env->ExceptionCheck()) {
+                        ALOGE("Exception dispatching batched input events.");
                         mBatchedInputEventPending = false; // try again later
                     }
                 }
@@ -172,46 +176,47 @@
         }
         assert(inputEvent);
 
-        jobject inputEventObj;
-        switch (inputEvent->getType()) {
-        case AINPUT_EVENT_TYPE_KEY:
+        if (!skipCallbacks) {
+            jobject inputEventObj;
+            switch (inputEvent->getType()) {
+            case AINPUT_EVENT_TYPE_KEY:
 #if DEBUG_DISPATCH_CYCLE
-            ALOGD("channel '%s' ~ Received key event.", getInputChannelName());
+                ALOGD("channel '%s' ~ Received key event.", getInputChannelName());
 #endif
-            inputEventObj = android_view_KeyEvent_fromNative(env,
-                    static_cast<KeyEvent*>(inputEvent));
-            mMessageQueue->raiseAndClearException(env, "new KeyEvent");
-            break;
+                inputEventObj = android_view_KeyEvent_fromNative(env,
+                        static_cast<KeyEvent*>(inputEvent));
+                break;
 
-        case AINPUT_EVENT_TYPE_MOTION:
+            case AINPUT_EVENT_TYPE_MOTION:
 #if DEBUG_DISPATCH_CYCLE
-            ALOGD("channel '%s' ~ Received motion event.", getInputChannelName());
+                ALOGD("channel '%s' ~ Received motion event.", getInputChannelName());
 #endif
-            inputEventObj = android_view_MotionEvent_obtainAsCopy(env,
-                    static_cast<MotionEvent*>(inputEvent));
-            mMessageQueue->raiseAndClearException(env, "new MotionEvent");
-            break;
+                inputEventObj = android_view_MotionEvent_obtainAsCopy(env,
+                        static_cast<MotionEvent*>(inputEvent));
+                break;
 
-        default:
-            assert(false); // InputConsumer should prevent this from ever happening
-            inputEventObj = NULL;
+            default:
+                assert(false); // InputConsumer should prevent this from ever happening
+                inputEventObj = NULL;
+            }
+
+            if (inputEventObj) {
+#if DEBUG_DISPATCH_CYCLE
+                ALOGD("channel '%s' ~ Dispatching input event.", getInputChannelName());
+#endif
+                env->CallVoidMethod(mReceiverObjGlobal,
+                        gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);
+                if (env->ExceptionCheck()) {
+                    ALOGE("Exception dispatching input event.");
+                    skipCallbacks = true;
+                }
+            } else {
+                ALOGW("channel '%s' ~ Failed to obtain event object.", getInputChannelName());
+                skipCallbacks = true;
+            }
         }
 
-        if (!inputEventObj) {
-            ALOGW("channel '%s' ~ Failed to obtain event object.", getInputChannelName());
-            mInputConsumer.sendFinishedSignal(seq, false);
-            continue;
-        }
-
-#if DEBUG_DISPATCH_CYCLE
-        ALOGD("channel '%s' ~ Dispatching input event.", getInputChannelName());
-#endif
-        env->CallVoidMethod(mReceiverObjGlobal,
-                gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);
-
-        env->DeleteLocalRef(inputEventObj);
-
-        if (mMessageQueue->raiseAndClearException(env, "dispatchInputEvent")) {
+        if (skipCallbacks) {
             mInputConsumer.sendFinishedSignal(seq, false);
         }
     }
@@ -268,8 +273,8 @@
 static void nativeConsumeBatchedInputEvents(JNIEnv* env, jclass clazz, jint receiverPtr) {
     sp<NativeInputEventReceiver> receiver =
             reinterpret_cast<NativeInputEventReceiver*>(receiverPtr);
-    status_t status = receiver->consumeEvents(true /*consumeBatches*/);
-    if (status && status != DEAD_OBJECT) {
+    status_t status = receiver->consumeEvents(env, true /*consumeBatches*/);
+    if (status && status != DEAD_OBJECT && !env->ExceptionCheck()) {
         String8 message;
         message.appendFormat("Failed to consume batched input event.  status=%d", status);
         jniThrowRuntimeException(env, message.string());