1 /*
2  * Copyright (C) 2010 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #define LOG_TAG "MessageQueue-JNI"
18 
19 #include <nativehelper/JNIHelp.h>
20 #include <android_runtime/AndroidRuntime.h>
21 
22 #include <utils/Looper.h>
23 #include <utils/Log.h>
24 #include "android_os_MessageQueue.h"
25 
26 #include "core_jni_helpers.h"
27 
28 namespace android {
29 
30 static struct {
31     jfieldID mPtr;   // native object attached to the DVM MessageQueue
32     jmethodID dispatchEvents;
33 } gMessageQueueClassInfo;
34 
35 // Must be kept in sync with the constants in Looper.FileDescriptorCallback
36 static const int CALLBACK_EVENT_INPUT = 1 << 0;
37 static const int CALLBACK_EVENT_OUTPUT = 1 << 1;
38 static const int CALLBACK_EVENT_ERROR = 1 << 2;
39 
40 
41 class NativeMessageQueue : public MessageQueue, public LooperCallback {
42 public:
43     NativeMessageQueue();
44     virtual ~NativeMessageQueue();
45 
46     virtual void raiseException(JNIEnv* env, const char* msg, jthrowable exceptionObj);
47 
48     void pollOnce(JNIEnv* env, jobject obj, int timeoutMillis);
49     void wake();
50     void setFileDescriptorEvents(int fd, int events);
51 
52     virtual int handleEvent(int fd, int events, void* data);
53 
54 private:
55     JNIEnv* mPollEnv;
56     jobject mPollObj;
57     jthrowable mExceptionObj;
58 };
59 
60 
MessageQueue()61 MessageQueue::MessageQueue() {
62 }
63 
~MessageQueue()64 MessageQueue::~MessageQueue() {
65 }
66 
raiseAndClearException(JNIEnv * env,const char * msg)67 bool MessageQueue::raiseAndClearException(JNIEnv* env, const char* msg) {
68     if (env->ExceptionCheck()) {
69         jthrowable exceptionObj = env->ExceptionOccurred();
70         env->ExceptionClear();
71         raiseException(env, msg, exceptionObj);
72         env->DeleteLocalRef(exceptionObj);
73         return true;
74     }
75     return false;
76 }
77 
NativeMessageQueue()78 NativeMessageQueue::NativeMessageQueue() :
79         mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {
80     mLooper = Looper::getForThread();
81     if (mLooper == NULL) {
82         mLooper = new Looper(false);
83         Looper::setForThread(mLooper);
84     }
85 }
86 
~NativeMessageQueue()87 NativeMessageQueue::~NativeMessageQueue() {
88 }
89 
raiseException(JNIEnv * env,const char * msg,jthrowable exceptionObj)90 void NativeMessageQueue::raiseException(JNIEnv* env, const char* msg, jthrowable exceptionObj) {
91     if (exceptionObj) {
92         if (mPollEnv == env) {
93             if (mExceptionObj) {
94                 env->DeleteLocalRef(mExceptionObj);
95             }
96             mExceptionObj = jthrowable(env->NewLocalRef(exceptionObj));
97             ALOGE("Exception in MessageQueue callback: %s", msg);
98             jniLogException(env, ANDROID_LOG_ERROR, LOG_TAG, exceptionObj);
99         } else {
100             ALOGE("Exception: %s", msg);
101             jniLogException(env, ANDROID_LOG_ERROR, LOG_TAG, exceptionObj);
102             LOG_ALWAYS_FATAL("raiseException() was called when not in a callback, exiting.");
103         }
104     }
105 }
106 
pollOnce(JNIEnv * env,jobject pollObj,int timeoutMillis)107 void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) {
108     mPollEnv = env;
109     mPollObj = pollObj;
110     mLooper->pollOnce(timeoutMillis);
111     mPollObj = NULL;
112     mPollEnv = NULL;
113 
114     if (mExceptionObj) {
115         env->Throw(mExceptionObj);
116         env->DeleteLocalRef(mExceptionObj);
117         mExceptionObj = NULL;
118     }
119 }
120 
wake()121 void NativeMessageQueue::wake() {
122     mLooper->wake();
123 }
124 
setFileDescriptorEvents(int fd,int events)125 void NativeMessageQueue::setFileDescriptorEvents(int fd, int events) {
126     if (events) {
127         int looperEvents = 0;
128         if (events & CALLBACK_EVENT_INPUT) {
129             looperEvents |= Looper::EVENT_INPUT;
130         }
131         if (events & CALLBACK_EVENT_OUTPUT) {
132             looperEvents |= Looper::EVENT_OUTPUT;
133         }
134         mLooper->addFd(fd, Looper::POLL_CALLBACK, looperEvents, this,
135                 reinterpret_cast<void*>(events));
136     } else {
137         mLooper->removeFd(fd);
138     }
139 }
140 
handleEvent(int fd,int looperEvents,void * data)141 int NativeMessageQueue::handleEvent(int fd, int looperEvents, void* data) {
142     int events = 0;
143     if (looperEvents & Looper::EVENT_INPUT) {
144         events |= CALLBACK_EVENT_INPUT;
145     }
146     if (looperEvents & Looper::EVENT_OUTPUT) {
147         events |= CALLBACK_EVENT_OUTPUT;
148     }
149     if (looperEvents & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP | Looper::EVENT_INVALID)) {
150         events |= CALLBACK_EVENT_ERROR;
151     }
152     int oldWatchedEvents = reinterpret_cast<intptr_t>(data);
153     int newWatchedEvents = mPollEnv->CallIntMethod(mPollObj,
154             gMessageQueueClassInfo.dispatchEvents, fd, events);
155     if (!newWatchedEvents) {
156         return 0; // unregister the fd
157     }
158     if (newWatchedEvents != oldWatchedEvents) {
159         setFileDescriptorEvents(fd, newWatchedEvents);
160     }
161     return 1;
162 }
163 
164 
165 // ----------------------------------------------------------------------------
166 
android_os_MessageQueue_getMessageQueue(JNIEnv * env,jobject messageQueueObj)167 sp<MessageQueue> android_os_MessageQueue_getMessageQueue(JNIEnv* env, jobject messageQueueObj) {
168     jlong ptr = env->GetLongField(messageQueueObj, gMessageQueueClassInfo.mPtr);
169     return reinterpret_cast<NativeMessageQueue*>(ptr);
170 }
171 
android_os_MessageQueue_nativeInit(JNIEnv * env,jclass clazz)172 static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
173     NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
174     if (!nativeMessageQueue) {
175         jniThrowRuntimeException(env, "Unable to allocate native queue");
176         return 0;
177     }
178 
179     nativeMessageQueue->incStrong(env);
180     return reinterpret_cast<jlong>(nativeMessageQueue);
181 }
182 
android_os_MessageQueue_nativeDestroy(JNIEnv * env,jclass clazz,jlong ptr)183 static void android_os_MessageQueue_nativeDestroy(JNIEnv* env, jclass clazz, jlong ptr) {
184     NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
185     nativeMessageQueue->decStrong(env);
186 }
187 
android_os_MessageQueue_nativePollOnce(JNIEnv * env,jobject obj,jlong ptr,jint timeoutMillis)188 static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
189         jlong ptr, jint timeoutMillis) {
190     NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
191     nativeMessageQueue->pollOnce(env, obj, timeoutMillis);
192 }
193 
android_os_MessageQueue_nativeWake(JNIEnv * env,jclass clazz,jlong ptr)194 static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jlong ptr) {
195     NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
196     nativeMessageQueue->wake();
197 }
198 
android_os_MessageQueue_nativeIsPolling(JNIEnv * env,jclass clazz,jlong ptr)199 static jboolean android_os_MessageQueue_nativeIsPolling(JNIEnv* env, jclass clazz, jlong ptr) {
200     NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
201     return nativeMessageQueue->getLooper()->isPolling();
202 }
203 
android_os_MessageQueue_nativeSetFileDescriptorEvents(JNIEnv * env,jclass clazz,jlong ptr,jint fd,jint events)204 static void android_os_MessageQueue_nativeSetFileDescriptorEvents(JNIEnv* env, jclass clazz,
205         jlong ptr, jint fd, jint events) {
206     NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
207     nativeMessageQueue->setFileDescriptorEvents(fd, events);
208 }
209 
210 // ----------------------------------------------------------------------------
211 
212 static const JNINativeMethod gMessageQueueMethods[] = {
213     /* name, signature, funcPtr */
214     { "nativeInit", "()J", (void*)android_os_MessageQueue_nativeInit },
215     { "nativeDestroy", "(J)V", (void*)android_os_MessageQueue_nativeDestroy },
216     { "nativePollOnce", "(JI)V", (void*)android_os_MessageQueue_nativePollOnce },
217     { "nativeWake", "(J)V", (void*)android_os_MessageQueue_nativeWake },
218     { "nativeIsPolling", "(J)Z", (void*)android_os_MessageQueue_nativeIsPolling },
219     { "nativeSetFileDescriptorEvents", "(JII)V",
220             (void*)android_os_MessageQueue_nativeSetFileDescriptorEvents },
221 };
222 
register_android_os_MessageQueue(JNIEnv * env)223 int register_android_os_MessageQueue(JNIEnv* env) {
224     int res = RegisterMethodsOrDie(env, "android/os/MessageQueue", gMessageQueueMethods,
225                                    NELEM(gMessageQueueMethods));
226 
227     jclass clazz = FindClassOrDie(env, "android/os/MessageQueue");
228     gMessageQueueClassInfo.mPtr = GetFieldIDOrDie(env, clazz, "mPtr", "J");
229     gMessageQueueClassInfo.dispatchEvents = GetMethodIDOrDie(env, clazz,
230             "dispatchEvents", "(II)I");
231 
232     return res;
233 }
234 
235 } // namespace android
236