Ensure that apps crash if they throw exceptions.

Previously, if an app threw an uncaught exception in an input,
vsync or native activity callback, it would log the exception then
continue limping merrily along.  In the case of input, it
could result in an ANR occurring because we had not drained
all of the pending input events and marked them as finished
(we only marked the most recent one finished).

Bug: 6304124
Change-Id: I87d76f7fd605e1a8af1237c66d8d62973080277e
diff --git a/core/jni/android_app_NativeActivity.cpp b/core/jni/android_app_NativeActivity.cpp
index 655a834..19bc154 100644
--- a/core/jni/android_app_NativeActivity.cpp
+++ b/core/jni/android_app_NativeActivity.cpp
@@ -416,8 +416,8 @@
         if (env != NULL && clazz != NULL) {
             env->DeleteGlobalRef(clazz);
         }
-        if (looper != NULL && mainWorkRead >= 0) {
-            looper->removeFd(mainWorkRead);
+        if (messageQueue != NULL && mainWorkRead >= 0) {
+            messageQueue->getLooper()->removeFd(mainWorkRead);
         }
         if (nativeInputQueue != NULL) {
             nativeInputQueue->mWorkWrite = -1;
@@ -481,7 +481,7 @@
     // These are used to wake up the main thread to process work.
     int mainWorkRead;
     int mainWorkWrite;
-    sp<Looper> looper;
+    sp<MessageQueue> messageQueue;
 };
 
 void android_NativeActivity_finish(ANativeActivity* activity) {
@@ -515,16 +515,6 @@
 
 // ------------------------------------------------------------------------
 
-static bool checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
-   if (env->ExceptionCheck()) {
-       ALOGE("An exception was thrown by callback '%s'.", methodName);
-       LOGE_EX(env);
-       env->ExceptionClear();
-       return true;
-   }
-   return false;
-}
-
 /*
  * Callback for handling native events on the application's main thread.
  */
@@ -551,7 +541,8 @@
                 if (inputEventObj) {
                     handled = code->env->CallBooleanMethod(code->clazz,
                             gNativeActivityClassInfo.dispatchUnhandledKeyEvent, inputEventObj);
-                    checkAndClearExceptionFromCallback(code->env, "dispatchUnhandledKeyEvent");
+                    code->messageQueue->raiseAndClearException(
+                            code->env, "dispatchUnhandledKeyEvent");
                     code->env->DeleteLocalRef(inputEventObj);
                 } else {
                     ALOGE("Failed to obtain key event for dispatchUnhandledKeyEvent.");
@@ -566,7 +557,7 @@
                 if (inputEventObj) {
                     code->env->CallVoidMethod(code->clazz,
                             gNativeActivityClassInfo.preDispatchKeyEvent, inputEventObj, seq);
-                    checkAndClearExceptionFromCallback(code->env, "preDispatchKeyEvent");
+                    code->messageQueue->raiseAndClearException(code->env, "preDispatchKeyEvent");
                     code->env->DeleteLocalRef(inputEventObj);
                 } else {
                     ALOGE("Failed to obtain key event for preDispatchKeyEvent.");
@@ -575,27 +566,27 @@
         } break;
         case CMD_FINISH: {
             code->env->CallVoidMethod(code->clazz, gNativeActivityClassInfo.finish);
-            checkAndClearExceptionFromCallback(code->env, "finish");
+            code->messageQueue->raiseAndClearException(code->env, "finish");
         } break;
         case CMD_SET_WINDOW_FORMAT: {
             code->env->CallVoidMethod(code->clazz,
                     gNativeActivityClassInfo.setWindowFormat, work.arg1);
-            checkAndClearExceptionFromCallback(code->env, "setWindowFormat");
+            code->messageQueue->raiseAndClearException(code->env, "setWindowFormat");
         } break;
         case CMD_SET_WINDOW_FLAGS: {
             code->env->CallVoidMethod(code->clazz,
                     gNativeActivityClassInfo.setWindowFlags, work.arg1, work.arg2);
-            checkAndClearExceptionFromCallback(code->env, "setWindowFlags");
+            code->messageQueue->raiseAndClearException(code->env, "setWindowFlags");
         } break;
         case CMD_SHOW_SOFT_INPUT: {
             code->env->CallVoidMethod(code->clazz,
                     gNativeActivityClassInfo.showIme, work.arg1);
-            checkAndClearExceptionFromCallback(code->env, "showIme");
+            code->messageQueue->raiseAndClearException(code->env, "showIme");
         } break;
         case CMD_HIDE_SOFT_INPUT: {
             code->env->CallVoidMethod(code->clazz,
                     gNativeActivityClassInfo.hideIme, work.arg1);
-            checkAndClearExceptionFromCallback(code->env, "hideIme");
+            code->messageQueue->raiseAndClearException(code->env, "hideIme");
         } break;
         default:
             ALOGW("Unknown work command: %d", work.cmd);
@@ -634,9 +625,9 @@
             return 0;
         }
         
-        code->looper = android_os_MessageQueue_getLooper(env, messageQueue);
-        if (code->looper == NULL) {
-            ALOGW("Unable to retrieve MessageQueue's Looper");
+        code->messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueue);
+        if (code->messageQueue == NULL) {
+            ALOGW("Unable to retrieve native MessageQueue");
             delete code;
             return 0;
         }
@@ -655,7 +646,8 @@
         result = fcntl(code->mainWorkWrite, F_SETFL, O_NONBLOCK);
         SLOGW_IF(result != 0, "Could not make main work write pipe "
                 "non-blocking: %s", strerror(errno));
-        code->looper->addFd(code->mainWorkRead, 0, ALOOPER_EVENT_INPUT, mainWorkCallback, code);
+        code->messageQueue->getLooper()->addFd(
+                code->mainWorkRead, 0, ALOOPER_EVENT_INPUT, mainWorkCallback, code);
         
         code->ANativeActivity::callbacks = &code->callbacks;
         if (env->GetJavaVM(&code->vm) < 0) {
diff --git a/core/jni/android_os_MessageQueue.cpp b/core/jni/android_os_MessageQueue.cpp
index 12a77d5..a4dcac6 100644
--- a/core/jni/android_os_MessageQueue.cpp
+++ b/core/jni/android_os_MessageQueue.cpp
@@ -17,6 +17,7 @@
 #define LOG_TAG "MessageQueue-JNI"
 
 #include "JNIHelp.h"
+#include <android_runtime/AndroidRuntime.h>
 
 #include <utils/Looper.h>
 #include <utils/Log.h>
@@ -24,31 +25,46 @@
 
 namespace android {
 
-// ----------------------------------------------------------------------------
-
 static struct {
     jfieldID mPtr;   // native object attached to the DVM MessageQueue
 } gMessageQueueClassInfo;
 
-// ----------------------------------------------------------------------------
 
-class NativeMessageQueue {
+class NativeMessageQueue : public MessageQueue {
 public:
     NativeMessageQueue();
-    ~NativeMessageQueue();
+    virtual ~NativeMessageQueue();
 
-    inline sp<Looper> getLooper() { return mLooper; }
+    virtual void raiseException(JNIEnv* env, const char* msg, jthrowable exceptionObj);
 
-    void pollOnce(int timeoutMillis);
+    void pollOnce(JNIEnv* env, int timeoutMillis);
+
     void wake();
 
 private:
-    sp<Looper> mLooper;
+    bool mInCallback;
+    jthrowable mExceptionObj;
 };
 
-// ----------------------------------------------------------------------------
 
-NativeMessageQueue::NativeMessageQueue() {
+MessageQueue::MessageQueue() {
+}
+
+MessageQueue::~MessageQueue() {
+}
+
+bool MessageQueue::raiseAndClearException(JNIEnv* env, const char* msg) {
+    jthrowable exceptionObj = env->ExceptionOccurred();
+    if (exceptionObj) {
+        env->ExceptionClear();
+        raiseException(env, msg, exceptionObj);
+        env->DeleteLocalRef(exceptionObj);
+        return true;
+    }
+    return false;
+}
+
+NativeMessageQueue::NativeMessageQueue() : mInCallback(false), mExceptionObj(NULL) {
     mLooper = Looper::getForThread();
     if (mLooper == NULL) {
         mLooper = new Looper(false);
@@ -59,8 +75,32 @@
 NativeMessageQueue::~NativeMessageQueue() {
 }
 
-void NativeMessageQueue::pollOnce(int timeoutMillis) {
+void NativeMessageQueue::raiseException(JNIEnv* env, const char* msg, jthrowable exceptionObj) {
+    if (exceptionObj) {
+        if (mInCallback) {
+            if (mExceptionObj) {
+                env->DeleteLocalRef(mExceptionObj);
+            }
+            mExceptionObj = jthrowable(env->NewLocalRef(exceptionObj));
+            ALOGE("Exception in MessageQueue callback: %s", msg);
+            jniLogException(env, ANDROID_LOG_ERROR, LOG_TAG, exceptionObj);
+        } else {
+            ALOGE("Exception: %s", msg);
+            jniLogException(env, ANDROID_LOG_ERROR, LOG_TAG, exceptionObj);
+            LOG_ALWAYS_FATAL("raiseException() was called when not in a callback, exiting.");
+        }
+    }
+}
+
+void NativeMessageQueue::pollOnce(JNIEnv* env, int timeoutMillis) {
+    mInCallback = true;
     mLooper->pollOnce(timeoutMillis);
+    mInCallback = false;
+    if (mExceptionObj) {
+        env->Throw(mExceptionObj);
+        env->DeleteLocalRef(mExceptionObj);
+        mExceptionObj = NULL;
+    }
 }
 
 void NativeMessageQueue::wake() {
@@ -81,19 +121,20 @@
              reinterpret_cast<jint>(nativeMessageQueue));
 }
 
-sp<Looper> android_os_MessageQueue_getLooper(JNIEnv* env, jobject messageQueueObj) {
+sp<MessageQueue> android_os_MessageQueue_getMessageQueue(JNIEnv* env, jobject messageQueueObj) {
     NativeMessageQueue* nativeMessageQueue =
             android_os_MessageQueue_getNativeMessageQueue(env, messageQueueObj);
-    return nativeMessageQueue != NULL ? nativeMessageQueue->getLooper() : NULL;
+    return nativeMessageQueue;
 }
 
 static void android_os_MessageQueue_nativeInit(JNIEnv* env, jobject obj) {
     NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
-    if (! nativeMessageQueue) {
+    if (!nativeMessageQueue) {
         jniThrowRuntimeException(env, "Unable to allocate native queue");
         return;
     }
 
+    nativeMessageQueue->incStrong(env);
     android_os_MessageQueue_setNativeMessageQueue(env, obj, nativeMessageQueue);
 }
 
@@ -102,7 +143,7 @@
             android_os_MessageQueue_getNativeMessageQueue(env, obj);
     if (nativeMessageQueue) {
         android_os_MessageQueue_setNativeMessageQueue(env, obj, NULL);
-        delete nativeMessageQueue;
+        nativeMessageQueue->decStrong(env);
     }
 }
 
@@ -113,7 +154,7 @@
 static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
         jint ptr, jint timeoutMillis) {
     NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
-    nativeMessageQueue->pollOnce(timeoutMillis);
+    nativeMessageQueue->pollOnce(env, timeoutMillis);
 }
 
 static void android_os_MessageQueue_nativeWake(JNIEnv* env, jobject obj, jint ptr) {
diff --git a/core/jni/android_os_MessageQueue.h b/core/jni/android_os_MessageQueue.h
index f961d8f..49d2aa0 100644
--- a/core/jni/android_os_MessageQueue.h
+++ b/core/jni/android_os_MessageQueue.h
@@ -18,12 +18,53 @@
 #define _ANDROID_OS_MESSAGEQUEUE_H
 
 #include "jni.h"
+#include <utils/Looper.h>
 
 namespace android {
 
-class Looper;
+class MessageQueue : public RefBase {
+public:
+    /* Gets the message queue's looper. */
+    inline sp<Looper> getLooper() const {
+        return mLooper;
+    }
 
-extern sp<Looper> android_os_MessageQueue_getLooper(JNIEnv* env, jobject messageQueueObj);
+    /* Checks whether the JNI environment has a pending exception.
+     *
+     * If an exception occurred, logs it together with the specified message,
+     * and calls raiseException() to ensure the exception will be raised when
+     * the callback returns, clears the pending exception from the environment,
+     * then returns true.
+     *
+     * If no exception occurred, returns false.
+     */
+    bool raiseAndClearException(JNIEnv* env, const char* msg);
+
+    /* Raises an exception from within a callback function.
+     * The exception will be rethrown when control returns to the message queue which
+     * will typically cause the application to crash.
+     *
+     * This message can only be called from within a callback function.  If it is called
+     * at any other time, the process will simply be killed.
+     *
+     * Does nothing if exception is NULL.
+     *
+     * (This method does not take ownership of the exception object reference.
+     * The caller is responsible for releasing its reference when it is done.)
+     */
+    virtual void raiseException(JNIEnv* env, const char* msg, jthrowable exceptionObj) = 0;
+
+protected:
+    MessageQueue();
+    virtual ~MessageQueue();
+
+protected:
+    sp<Looper> mLooper;
+};
+
+/* Gets the native object associated with a MessageQueue. */
+extern sp<MessageQueue> android_os_MessageQueue_getMessageQueue(
+        JNIEnv* env, jobject messageQueueObj);
 
 } // namespace android
 
diff --git a/core/jni/android_view_DisplayEventReceiver.cpp b/core/jni/android_view_DisplayEventReceiver.cpp
index 72c171c..d80bfb3 100644
--- a/core/jni/android_view_DisplayEventReceiver.cpp
+++ b/core/jni/android_view_DisplayEventReceiver.cpp
@@ -45,7 +45,7 @@
 class NativeDisplayEventReceiver : public RefBase {
 public:
     NativeDisplayEventReceiver(JNIEnv* env,
-            jobject receiverObj, const sp<Looper>& looper);
+            jobject receiverObj, const sp<MessageQueue>& messageQueue);
 
     status_t initialize();
     status_t scheduleVsync();
@@ -55,7 +55,7 @@
 
 private:
     jobject mReceiverObjGlobal;
-    sp<Looper> mLooper;
+    sp<MessageQueue> mMessageQueue;
     DisplayEventReceiver mReceiver;
     bool mWaitingForVsync;
 
@@ -65,9 +65,9 @@
 
 
 NativeDisplayEventReceiver::NativeDisplayEventReceiver(JNIEnv* env,
-        jobject receiverObj, const sp<Looper>& looper) :
+        jobject receiverObj, const sp<MessageQueue>& messageQueue) :
         mReceiverObjGlobal(env->NewGlobalRef(receiverObj)),
-        mLooper(looper), mWaitingForVsync(false) {
+        mMessageQueue(messageQueue), mWaitingForVsync(false) {
     ALOGV("receiver %p ~ Initializing input event receiver.", this);
 }
 
@@ -75,7 +75,7 @@
     ALOGV("receiver %p ~ Disposing display event receiver.", this);
 
     if (!mReceiver.initCheck()) {
-        mLooper->removeFd(mReceiver.getFd());
+        mMessageQueue->getLooper()->removeFd(mReceiver.getFd());
     }
 
     JNIEnv* env = AndroidRuntime::getJNIEnv();
@@ -89,7 +89,7 @@
         return result;
     }
 
-    int rc = mLooper->addFd(mReceiver.getFd(), 0, ALOOPER_EVENT_INPUT,
+    int rc = mMessageQueue->getLooper()->addFd(mReceiver.getFd(), 0, ALOOPER_EVENT_INPUT,
             handleReceiveCallback, this);
     if (rc < 0) {
         return UNKNOWN_ERROR;
@@ -151,12 +151,7 @@
             gDisplayEventReceiverClassInfo.dispatchVsync, vsyncTimestamp, vsyncCount);
     ALOGV("receiver %p ~ Returned from vsync handler.", data);
 
-    if (env->ExceptionCheck()) {
-        ALOGE("An exception occurred while dispatching a vsync event.");
-        LOGE_EX(env);
-        env->ExceptionClear();
-    }
-
+    r->mMessageQueue->raiseAndClearException(env, "dispatchVsync");
     return 1; // keep the callback
 }
 
@@ -183,14 +178,14 @@
 
 static jint nativeInit(JNIEnv* env, jclass clazz, jobject receiverObj,
         jobject messageQueueObj) {
-    sp<Looper> looper = android_os_MessageQueue_getLooper(env, messageQueueObj);
-    if (looper == NULL) {
+    sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
+    if (messageQueue == NULL) {
         jniThrowRuntimeException(env, "MessageQueue is not initialized.");
         return 0;
     }
 
     sp<NativeDisplayEventReceiver> receiver = new NativeDisplayEventReceiver(env,
-            receiverObj, looper);
+            receiverObj, messageQueue);
     status_t status = receiver->initialize();
     if (status) {
         String8 message;
diff --git a/core/jni/android_view_InputEventReceiver.cpp b/core/jni/android_view_InputEventReceiver.cpp
index e7d4244..348437d 100644
--- a/core/jni/android_view_InputEventReceiver.cpp
+++ b/core/jni/android_view_InputEventReceiver.cpp
@@ -48,7 +48,7 @@
 public:
     NativeInputEventReceiver(JNIEnv* env,
             jobject receiverObj, const sp<InputChannel>& inputChannel,
-            const sp<Looper>& looper);
+            const sp<MessageQueue>& messageQueue);
 
     status_t initialize();
     status_t finishInputEvent(uint32_t seq, bool handled);
@@ -61,7 +61,7 @@
 private:
     jobject mReceiverObjGlobal;
     InputConsumer mInputConsumer;
-    sp<Looper> mLooper;
+    sp<MessageQueue> mMessageQueue;
     PreallocatedInputEventFactory mInputEventFactory;
     bool mBatchedInputEventPending;
 
@@ -72,9 +72,10 @@
 
 
 NativeInputEventReceiver::NativeInputEventReceiver(JNIEnv* env,
-        jobject receiverObj, const sp<InputChannel>& inputChannel, const sp<Looper>& looper) :
+        jobject receiverObj, const sp<InputChannel>& inputChannel,
+        const sp<MessageQueue>& messageQueue) :
         mReceiverObjGlobal(env->NewGlobalRef(receiverObj)),
-        mInputConsumer(inputChannel), mLooper(looper),
+        mInputConsumer(inputChannel), mMessageQueue(messageQueue),
         mBatchedInputEventPending(false) {
 #if DEBUG_DISPATCH_CYCLE
     ALOGD("channel '%s' ~ Initializing input event receiver.", getInputChannelName());
@@ -86,7 +87,7 @@
     ALOGD("channel '%s' ~ Disposing input event receiver.", getInputChannelName());
 #endif
 
-    mLooper->removeFd(mInputConsumer.getChannel()->getFd());
+    mMessageQueue->getLooper()->removeFd(mInputConsumer.getChannel()->getFd());
 
     JNIEnv* env = AndroidRuntime::getJNIEnv();
     env->DeleteGlobalRef(mReceiverObjGlobal);
@@ -94,7 +95,8 @@
 
 status_t NativeInputEventReceiver::initialize() {
     int receiveFd = mInputConsumer.getChannel()->getFd();
-    mLooper->addFd(receiveFd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
+    mMessageQueue->getLooper()->addFd(
+            receiveFd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
     return OK;
 }
 
@@ -157,12 +159,8 @@
 #endif
                     env->CallVoidMethod(mReceiverObjGlobal,
                             gInputEventReceiverClassInfo.dispatchBatchedInputEventPending);
-
-                    if (env->ExceptionCheck()) {
-                        ALOGE("channel '%s' ~ An exception occurred while dispatching that "
-                                "batched input events are pending.", getInputChannelName());
-                        LOGE_EX(env);
-                        env->ExceptionClear();
+                    if (mMessageQueue->raiseAndClearException(
+                            env, "dispatchBatchedInputEventPending")) {
                         mBatchedInputEventPending = false; // try again later
                     }
                 }
@@ -182,6 +180,7 @@
 #endif
             inputEventObj = android_view_KeyEvent_fromNative(env,
                     static_cast<KeyEvent*>(inputEvent));
+            mMessageQueue->raiseAndClearException(env, "new KeyEvent");
             break;
 
         case AINPUT_EVENT_TYPE_MOTION:
@@ -190,6 +189,7 @@
 #endif
             inputEventObj = android_view_MotionEvent_obtainAsCopy(env,
                     static_cast<MotionEvent*>(inputEvent));
+            mMessageQueue->raiseAndClearException(env, "new MotionEvent");
             break;
 
         default:
@@ -200,7 +200,7 @@
         if (!inputEventObj) {
             ALOGW("channel '%s' ~ Failed to obtain event object.", getInputChannelName());
             mInputConsumer.sendFinishedSignal(seq, false);
-            return NO_MEMORY;
+            continue;
         }
 
 #if DEBUG_DISPATCH_CYCLE
@@ -211,14 +211,8 @@
 
         env->DeleteLocalRef(inputEventObj);
 
-        if (env->ExceptionCheck()) {
-            ALOGE("channel '%s' ~ An exception occurred while dispatching an event.",
-                    getInputChannelName());
-            LOGE_EX(env);
-            env->ExceptionClear();
-
+        if (mMessageQueue->raiseAndClearException(env, "dispatchInputEvent")) {
             mInputConsumer.sendFinishedSignal(seq, false);
-            return OK;
         }
     }
 }
@@ -233,14 +227,14 @@
         return 0;
     }
 
-    sp<Looper> looper = android_os_MessageQueue_getLooper(env, messageQueueObj);
-    if (looper == NULL) {
+    sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
+    if (messageQueue == NULL) {
         jniThrowRuntimeException(env, "MessageQueue is not initialized.");
         return 0;
     }
 
     sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,
-            receiverObj, inputChannel, looper);
+            receiverObj, inputChannel, messageQueue);
     status_t status = receiver->initialize();
     if (status) {
         String8 message;