/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define LOG_TAG "MessageQueue-JNI" #include "JNIHelp.h" #include #include #include #include "android_os_MessageQueue.h" #include "core_jni_helpers.h" namespace android { static struct { jfieldID mPtr; // native object attached to the DVM MessageQueue jmethodID dispatchEvents; } gMessageQueueClassInfo; // Must be kept in sync with the constants in Looper.FileDescriptorCallback static const int CALLBACK_EVENT_INPUT = 1 << 0; static const int CALLBACK_EVENT_OUTPUT = 1 << 1; static const int CALLBACK_EVENT_ERROR = 1 << 2; class NativeMessageQueue : public MessageQueue, public LooperCallback { public: NativeMessageQueue(); virtual ~NativeMessageQueue(); virtual void raiseException(JNIEnv* env, const char* msg, jthrowable exceptionObj); void pollOnce(JNIEnv* env, jobject obj, int timeoutMillis); void wake(); void setFileDescriptorEvents(int fd, int events); virtual int handleEvent(int fd, int events, void* data); private: JNIEnv* mPollEnv; jobject mPollObj; jthrowable mExceptionObj; }; MessageQueue::MessageQueue() { } MessageQueue::~MessageQueue() { } bool MessageQueue::raiseAndClearException(JNIEnv* env, const char* msg) { if (env->ExceptionCheck()) { jthrowable exceptionObj = env->ExceptionOccurred(); env->ExceptionClear(); raiseException(env, msg, exceptionObj); env->DeleteLocalRef(exceptionObj); return true; } return false; } NativeMessageQueue::NativeMessageQueue() : mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) { mLooper = Looper::getForThread(); if (mLooper == NULL) { mLooper = new Looper(false); Looper::setForThread(mLooper); } } NativeMessageQueue::~NativeMessageQueue() { } void NativeMessageQueue::raiseException(JNIEnv* env, const char* msg, jthrowable exceptionObj) { if (exceptionObj) { if (mPollEnv == env) { 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, jobject pollObj, int timeoutMillis) { mPollEnv = env; mPollObj = pollObj; mLooper->pollOnce(timeoutMillis); mPollObj = NULL; mPollEnv = NULL; if (mExceptionObj) { env->Throw(mExceptionObj); env->DeleteLocalRef(mExceptionObj); mExceptionObj = NULL; } } void NativeMessageQueue::wake() { mLooper->wake(); } void NativeMessageQueue::setFileDescriptorEvents(int fd, int events) { if (events) { int looperEvents = 0; if (events & CALLBACK_EVENT_INPUT) { looperEvents |= Looper::EVENT_INPUT; } if (events & CALLBACK_EVENT_OUTPUT) { looperEvents |= Looper::EVENT_OUTPUT; } mLooper->addFd(fd, Looper::POLL_CALLBACK, looperEvents, this, reinterpret_cast(events)); } else { mLooper->removeFd(fd); } } int NativeMessageQueue::handleEvent(int fd, int looperEvents, void* data) { int events = 0; if (looperEvents & Looper::EVENT_INPUT) { events |= CALLBACK_EVENT_INPUT; } if (looperEvents & Looper::EVENT_OUTPUT) { events |= CALLBACK_EVENT_OUTPUT; } if (looperEvents & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP | Looper::EVENT_INVALID)) { events |= CALLBACK_EVENT_ERROR; } int oldWatchedEvents = reinterpret_cast(data); int newWatchedEvents = mPollEnv->CallIntMethod(mPollObj, gMessageQueueClassInfo.dispatchEvents, fd, events); if (!newWatchedEvents) { return 0; // unregister the fd } if (newWatchedEvents != oldWatchedEvents) { setFileDescriptorEvents(fd, newWatchedEvents); } return 1; } // ---------------------------------------------------------------------------- sp android_os_MessageQueue_getMessageQueue(JNIEnv* env, jobject messageQueueObj) { jlong ptr = env->GetLongField(messageQueueObj, gMessageQueueClassInfo.mPtr); return reinterpret_cast(ptr); } static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) { NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue(); if (!nativeMessageQueue) { jniThrowRuntimeException(env, "Unable to allocate native queue"); return 0; } nativeMessageQueue->incStrong(env); return reinterpret_cast(nativeMessageQueue); } static void android_os_MessageQueue_nativeDestroy(JNIEnv* env, jclass clazz, jlong ptr) { NativeMessageQueue* nativeMessageQueue = reinterpret_cast(ptr); nativeMessageQueue->decStrong(env); } static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj, jlong ptr, jint timeoutMillis) { NativeMessageQueue* nativeMessageQueue = reinterpret_cast(ptr); nativeMessageQueue->pollOnce(env, obj, timeoutMillis); } static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jlong ptr) { NativeMessageQueue* nativeMessageQueue = reinterpret_cast(ptr); nativeMessageQueue->wake(); } static jboolean android_os_MessageQueue_nativeIsPolling(JNIEnv* env, jclass clazz, jlong ptr) { NativeMessageQueue* nativeMessageQueue = reinterpret_cast(ptr); return nativeMessageQueue->getLooper()->isPolling(); } static void android_os_MessageQueue_nativeSetFileDescriptorEvents(JNIEnv* env, jclass clazz, jlong ptr, jint fd, jint events) { NativeMessageQueue* nativeMessageQueue = reinterpret_cast(ptr); nativeMessageQueue->setFileDescriptorEvents(fd, events); } // ---------------------------------------------------------------------------- static JNINativeMethod gMessageQueueMethods[] = { /* name, signature, funcPtr */ { "nativeInit", "()J", (void*)android_os_MessageQueue_nativeInit }, { "nativeDestroy", "(J)V", (void*)android_os_MessageQueue_nativeDestroy }, { "nativePollOnce", "(JI)V", (void*)android_os_MessageQueue_nativePollOnce }, { "nativeWake", "(J)V", (void*)android_os_MessageQueue_nativeWake }, { "nativeIsPolling", "(J)Z", (void*)android_os_MessageQueue_nativeIsPolling }, { "nativeSetFileDescriptorEvents", "(JII)V", (void*)android_os_MessageQueue_nativeSetFileDescriptorEvents }, }; int register_android_os_MessageQueue(JNIEnv* env) { int res = RegisterMethodsOrDie(env, "android/os/MessageQueue", gMessageQueueMethods, NELEM(gMessageQueueMethods)); jclass clazz = FindClassOrDie(env, "android/os/MessageQueue"); gMessageQueueClassInfo.mPtr = GetFieldIDOrDie(env, clazz, "mPtr", "J"); gMessageQueueClassInfo.dispatchEvents = GetMethodIDOrDie(env, clazz, "dispatchEvents", "(II)I"); return res; } } // namespace android