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.");
        }
    }
}
static void System_log(JNIEnv* env, jclass, jchar type, jstring javaMessage, jthrowable exception) {
    ScopedUtfChars message(env, javaMessage);
    if (message.c_str() == NULL) {
        // Since this function is used for last-gasp debugging output, be noisy on failure.
        ALOGE("message.c_str() == NULL");
        return;
    }
    int priority;
    switch (type) {
    case 'D': case 'd': priority = ANDROID_LOG_DEBUG;   break;
    case 'E': case 'e': priority = ANDROID_LOG_ERROR;   break;
    case 'F': case 'f': priority = ANDROID_LOG_FATAL;   break;
    case 'I': case 'i': priority = ANDROID_LOG_INFO;    break;
    case 'S': case 's': priority = ANDROID_LOG_SILENT;  break;
    case 'V': case 'v': priority = ANDROID_LOG_VERBOSE; break;
    case 'W': case 'w': priority = ANDROID_LOG_WARN;    break;
    default:            priority = ANDROID_LOG_DEFAULT; break;
    }
    LOG_PRI(priority, LOG_TAG, "%s", message.c_str());
    if (exception != NULL) {
        jniLogException(env, priority, LOG_TAG, exception);
    }
}