Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 12578bde authored by Jeff Brown's avatar Jeff Brown Committed by Android (Google) Code Review
Browse files

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

parents cd837070 603b4458
Loading
Loading
Loading
Loading
+16 −24
Original line number Diff line number Diff line
@@ -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) {
+57 −16
Original line number Diff line number Diff line
@@ -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,10 +121,10 @@ 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) {
@@ -94,6 +134,7 @@ static void android_os_MessageQueue_nativeInit(JNIEnv* env, jobject obj) {
        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) {
+43 −2
Original line number Diff line number Diff line
@@ -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

+10 −15
Original line number Diff line number Diff line
@@ -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;
+17 −23
Original line number Diff line number Diff line
@@ -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;
Loading