Merge "Ensure that apps crash if they throw exceptions."

This commit is contained in:
Jeff Brown
2012-04-06 18:26:48 -07:00
committed by Android (Google) Code Review
6 changed files with 146 additions and 82 deletions

View File

@@ -416,8 +416,8 @@ struct NativeCode : public ANativeActivity {
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 @@ struct NativeCode : public ANativeActivity {
// 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 @@ void android_NativeActivity_hideSoftInput(
// ------------------------------------------------------------------------
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 @@ static int mainWorkCallback(int fd, int events, void* data) {
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 @@ static int mainWorkCallback(int fd, int events, void* data) {
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 @@ static int mainWorkCallback(int fd, int events, void* data) {
} 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 @@ loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path, jstring funcName
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 @@ loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path, jstring funcName
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) {

View File

@@ -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(JNIEnv* env, int timeoutMillis);
void pollOnce(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() {
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 @@ static void android_os_MessageQueue_setNativeMessageQueue(JNIEnv* env, jobject m
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 @@ static void android_os_MessageQueue_nativeDestroy(JNIEnv* env, jobject obj) {
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 throwQueueNotInitialized(JNIEnv* env) {
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) {

View File

@@ -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

View File

@@ -45,7 +45,7 @@ static struct {
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 @@ protected:
private:
jobject mReceiverObjGlobal;
sp<Looper> mLooper;
sp<MessageQueue> mMessageQueue;
DisplayEventReceiver mReceiver;
bool mWaitingForVsync;
@@ -65,9 +65,9 @@ private:
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 @@ NativeDisplayEventReceiver::~NativeDisplayEventReceiver() {
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 @@ status_t NativeDisplayEventReceiver::initialize() {
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 @@ int NativeDisplayEventReceiver::handleReceiveCallback(int receiveFd, int events,
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 @@ bool NativeDisplayEventReceiver::readLastVsyncMessage(
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;

View File

@@ -48,7 +48,7 @@ class NativeInputEventReceiver : public RefBase {
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 @@ protected:
private:
jobject mReceiverObjGlobal;
InputConsumer mInputConsumer;
sp<Looper> mLooper;
sp<MessageQueue> mMessageQueue;
PreallocatedInputEventFactory mInputEventFactory;
bool mBatchedInputEventPending;
@@ -72,9 +72,10 @@ private:
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 @@ NativeInputEventReceiver::~NativeInputEventReceiver() {
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 @@ NativeInputEventReceiver::~NativeInputEventReceiver() {
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 @@ status_t NativeInputEventReceiver::consumeEvents(bool consumeBatches) {
#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 @@ status_t NativeInputEventReceiver::consumeEvents(bool consumeBatches) {
#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 @@ status_t NativeInputEventReceiver::consumeEvents(bool consumeBatches) {
#endif
inputEventObj = android_view_MotionEvent_obtainAsCopy(env,
static_cast<MotionEvent*>(inputEvent));
mMessageQueue->raiseAndClearException(env, "new MotionEvent");
break;
default:
@@ -200,7 +200,7 @@ status_t NativeInputEventReceiver::consumeEvents(bool consumeBatches) {
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 @@ status_t NativeInputEventReceiver::consumeEvents(bool consumeBatches) {
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 @@ static jint nativeInit(JNIEnv* env, jclass clazz, jobject receiverObj,
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;

View File

@@ -950,8 +950,9 @@ void NativeInputManager::loadPointerResources(PointerResources* outResources) {
static jint nativeInit(JNIEnv* env, jclass clazz,
jobject serviceObj, jobject contextObj, jobject messageQueueObj) {
sp<Looper> looper = android_os_MessageQueue_getLooper(env, messageQueueObj);
NativeInputManager* im = new NativeInputManager(contextObj, serviceObj, looper);
sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
NativeInputManager* im = new NativeInputManager(contextObj, serviceObj,
messageQueue->getLooper());
im->incStrong(serviceObj);
return reinterpret_cast<jint>(im);
}