/*
|
* 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 <nativehelper/JNIHelp.h>
|
#include <android_runtime/AndroidRuntime.h>
|
|
#include <utils/Looper.h>
|
#include <utils/Log.h>
|
#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<void*>(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<intptr_t>(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<MessageQueue> android_os_MessageQueue_getMessageQueue(JNIEnv* env, jobject messageQueueObj) {
|
jlong ptr = env->GetLongField(messageQueueObj, gMessageQueueClassInfo.mPtr);
|
return reinterpret_cast<NativeMessageQueue*>(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<jlong>(nativeMessageQueue);
|
}
|
|
static void android_os_MessageQueue_nativeDestroy(JNIEnv* env, jclass clazz, jlong ptr) {
|
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
|
nativeMessageQueue->decStrong(env);
|
}
|
|
static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
|
jlong ptr, jint timeoutMillis) {
|
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
|
nativeMessageQueue->pollOnce(env, obj, timeoutMillis);
|
}
|
|
static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jlong ptr) {
|
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
|
nativeMessageQueue->wake();
|
}
|
|
static jboolean android_os_MessageQueue_nativeIsPolling(JNIEnv* env, jclass clazz, jlong ptr) {
|
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(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<NativeMessageQueue*>(ptr);
|
nativeMessageQueue->setFileDescriptorEvents(fd, events);
|
}
|
|
// ----------------------------------------------------------------------------
|
|
static const 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
|