android_app_NativeActivity.cpp revision 54a181b1a2b1517a9479b21fbf7705a688232faf
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 "NativeActivity"
18#include <utils/Log.h>
19
20#include <poll.h>
21#include <dlfcn.h>
22
23#include <android_runtime/AndroidRuntime.h>
24#include <android/native_activity.h>
25#include <surfaceflinger/Surface.h>
26#include <ui/egl/android_natives.h>
27#include <ui/InputTransport.h>
28#include <utils/PollLoop.h>
29
30#include "JNIHelp.h"
31#include "android_os_MessageQueue.h"
32#include "android_view_InputChannel.h"
33#include "android_view_KeyEvent.h"
34#include "android_view_Surface.h"
35
36namespace android
37{
38
39static struct {
40    jclass clazz;
41
42    jmethodID dispatchUnhandledKeyEvent;
43    jmethodID setWindowFlags;
44    jmethodID setWindowFormat;
45} gNativeActivityClassInfo;
46
47// ------------------------------------------------------------------------
48
49/*
50 * Specialized input queue that allows unhandled key events to be dispatched
51 * back to the native activity's Java framework code.
52 */
53struct MyInputQueue : AInputQueue {
54    explicit MyInputQueue(const android::sp<android::InputChannel>& channel, int workWrite)
55        : AInputQueue(channel), mWorkWrite(workWrite) {
56    }
57
58    virtual void doDefaultKey(android::KeyEvent* keyEvent) {
59        mLock.lock();
60        LOGI("Default key: pending=%d write=%d\n", mPendingKeys.size(), mWorkWrite);
61        if (mPendingKeys.size() <= 0 && mWorkWrite >= 0) {
62            int8_t cmd = 1;
63            write(mWorkWrite, &cmd, sizeof(cmd));
64        }
65        mPendingKeys.add(keyEvent);
66        mLock.unlock();
67    }
68
69    KeyEvent* getNextEvent() {
70        KeyEvent* event = NULL;
71
72        mLock.lock();
73        if (mPendingKeys.size() > 0) {
74            event = mPendingKeys[0];
75            mPendingKeys.removeAt(0);
76        }
77        mLock.unlock();
78
79        return event;
80    }
81
82    int mWorkWrite;
83
84    Mutex mLock;
85    Vector<KeyEvent*> mPendingKeys;
86};
87
88// ------------------------------------------------------------------------
89
90/*
91 * Native state for interacting with the NativeActivity class.
92 */
93struct NativeCode {
94    NativeCode(void* _dlhandle, ANativeActivity_createFunc* _createFunc) {
95        memset(&activity, sizeof(activity), 0);
96        memset(&callbacks, sizeof(callbacks), 0);
97        dlhandle = _dlhandle;
98        createActivityFunc = _createFunc;
99        nativeWindow = NULL;
100        inputChannel = NULL;
101        nativeInputQueue = NULL;
102        mainWorkRead = mainWorkWrite = -1;
103    }
104
105    ~NativeCode() {
106        if (activity.env != NULL && activity.clazz != NULL) {
107            activity.env->DeleteGlobalRef(activity.clazz);
108        }
109        if (pollLoop != NULL && mainWorkRead >= 0) {
110            pollLoop->removeCallback(mainWorkRead);
111        }
112        if (nativeInputQueue != NULL) {
113            nativeInputQueue->mWorkWrite = -1;
114        }
115        setSurface(NULL);
116        setInputChannel(NULL);
117        if (callbacks.onDestroy != NULL) {
118            callbacks.onDestroy(&activity);
119        }
120        if (mainWorkRead >= 0) close(mainWorkRead);
121        if (mainWorkWrite >= 0) close(mainWorkWrite);
122        if (dlhandle != NULL) {
123            // for now don't unload...  we probably should clean this
124            // up and only keep one open dlhandle per proc, since there
125            // is really no benefit to unloading the code.
126            //dlclose(dlhandle);
127        }
128    }
129
130    void setSurface(jobject _surface) {
131        if (_surface != NULL) {
132            nativeWindow = android_Surface_getNativeWindow(activity.env, _surface);
133        } else {
134            nativeWindow = NULL;
135        }
136    }
137
138    status_t setInputChannel(jobject _channel) {
139        if (inputChannel != NULL) {
140            delete nativeInputQueue;
141            activity.env->DeleteGlobalRef(inputChannel);
142        }
143        inputChannel = NULL;
144        nativeInputQueue = NULL;
145        if (_channel != NULL) {
146            inputChannel = activity.env->NewGlobalRef(_channel);
147            sp<InputChannel> ic =
148                    android_view_InputChannel_getInputChannel(activity.env, _channel);
149            if (ic != NULL) {
150                nativeInputQueue = new MyInputQueue(ic, mainWorkWrite);
151                if (nativeInputQueue->getConsumer().initialize() != android::OK) {
152                    delete nativeInputQueue;
153                    nativeInputQueue = NULL;
154                    return UNKNOWN_ERROR;
155                }
156            } else {
157                return UNKNOWN_ERROR;
158            }
159        }
160        return OK;
161    }
162
163    ANativeActivity activity;
164    ANativeActivityCallbacks callbacks;
165
166    void* dlhandle;
167    ANativeActivity_createFunc* createActivityFunc;
168
169    sp<ANativeWindow> nativeWindow;
170    jobject inputChannel;
171    struct MyInputQueue* nativeInputQueue;
172
173    // These are used to wake up the main thread to process work.
174    int mainWorkRead;
175    int mainWorkWrite;
176    sp<PollLoop> pollLoop;
177};
178
179// ------------------------------------------------------------------------
180
181/*
182 * Callback for handling native events on the application's main thread.
183 */
184static bool mainWorkCallback(int fd, int events, void* data) {
185    NativeCode* code = (NativeCode*)data;
186    if ((events & POLLIN) != 0) {
187        KeyEvent* keyEvent;
188        while ((keyEvent=code->nativeInputQueue->getNextEvent()) != NULL) {
189            jobject inputEventObj = android_view_KeyEvent_fromNative(
190                    code->activity.env, keyEvent);
191            code->activity.env->CallVoidMethod(code->activity.clazz,
192                    gNativeActivityClassInfo.dispatchUnhandledKeyEvent, inputEventObj);
193            int32_t res = code->nativeInputQueue->getConsumer().sendFinishedSignal();
194            if (res != OK) {
195                LOGW("Failed to send finished signal on channel '%s'.  status=%d",
196                        code->nativeInputQueue->getConsumer().getChannel()->getName().string(), res);
197            }
198        }
199    }
200
201    return true;
202}
203
204// ------------------------------------------------------------------------
205
206static jint
207loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path, jobject messageQueue)
208{
209    const char* pathStr = env->GetStringUTFChars(path, NULL);
210    NativeCode* code = NULL;
211
212    void* handle = dlopen(pathStr, RTLD_LAZY);
213
214    env->ReleaseStringUTFChars(path, pathStr);
215
216    if (handle != NULL) {
217        code = new NativeCode(handle, (ANativeActivity_createFunc*)
218                dlsym(handle, "ANativeActivity_onCreate"));
219        if (code->createActivityFunc == NULL) {
220            LOGW("ANativeActivity_onCreate not found");
221            delete code;
222            return 0;
223        }
224
225        code->pollLoop = android_os_MessageQueue_getPollLoop(env, messageQueue);
226        if (code->pollLoop == NULL) {
227            LOGW("Unable to retrieve MessageQueue's PollLoop");
228            delete code;
229            return 0;
230        }
231
232        int msgpipe[2];
233        if (pipe(msgpipe)) {
234            LOGW("could not create pipe: %s", strerror(errno));
235            delete code;
236            return 0;
237        }
238        code->mainWorkRead = msgpipe[0];
239        code->mainWorkWrite = msgpipe[1];
240        code->pollLoop->setCallback(code->mainWorkRead, POLLIN, mainWorkCallback, code);
241
242        code->activity.callbacks = &code->callbacks;
243        if (env->GetJavaVM(&code->activity.vm) < 0) {
244            LOGW("NativeActivity GetJavaVM failed");
245            delete code;
246            return 0;
247        }
248        code->activity.env = env;
249        code->activity.clazz = env->NewGlobalRef(clazz);
250        code->createActivityFunc(&code->activity, NULL, 0);
251    }
252
253    return (jint)code;
254}
255
256static void
257unloadNativeCode_native(JNIEnv* env, jobject clazz, jint handle)
258{
259    if (handle != 0) {
260        NativeCode* code = (NativeCode*)handle;
261        delete code;
262    }
263}
264
265static void
266onStart_native(JNIEnv* env, jobject clazz, jint handle)
267{
268    if (handle != 0) {
269        NativeCode* code = (NativeCode*)handle;
270        if (code->callbacks.onStart != NULL) {
271            code->callbacks.onStart(&code->activity);
272        }
273    }
274}
275
276static void
277onResume_native(JNIEnv* env, jobject clazz, jint handle)
278{
279    if (handle != 0) {
280        NativeCode* code = (NativeCode*)handle;
281        if (code->callbacks.onResume != NULL) {
282            code->callbacks.onResume(&code->activity);
283        }
284    }
285}
286
287static void
288onSaveInstanceState_native(JNIEnv* env, jobject clazz, jint handle)
289{
290    if (handle != 0) {
291        NativeCode* code = (NativeCode*)handle;
292        if (code->callbacks.onSaveInstanceState != NULL) {
293            size_t len = 0;
294            code->callbacks.onSaveInstanceState(&code->activity, &len);
295        }
296    }
297}
298
299static void
300onPause_native(JNIEnv* env, jobject clazz, jint handle)
301{
302    if (handle != 0) {
303        NativeCode* code = (NativeCode*)handle;
304        if (code->callbacks.onPause != NULL) {
305            code->callbacks.onPause(&code->activity);
306        }
307    }
308}
309
310static void
311onStop_native(JNIEnv* env, jobject clazz, jint handle)
312{
313    if (handle != 0) {
314        NativeCode* code = (NativeCode*)handle;
315        if (code->callbacks.onStop != NULL) {
316            code->callbacks.onStop(&code->activity);
317        }
318    }
319}
320
321static void
322onLowMemory_native(JNIEnv* env, jobject clazz, jint handle)
323{
324    if (handle != 0) {
325        NativeCode* code = (NativeCode*)handle;
326        if (code->callbacks.onLowMemory != NULL) {
327            code->callbacks.onLowMemory(&code->activity);
328        }
329    }
330}
331
332static void
333onWindowFocusChanged_native(JNIEnv* env, jobject clazz, jint handle, jboolean focused)
334{
335    if (handle != 0) {
336        NativeCode* code = (NativeCode*)handle;
337        if (code->callbacks.onWindowFocusChanged != NULL) {
338            code->callbacks.onWindowFocusChanged(&code->activity, focused ? 1 : 0);
339        }
340    }
341}
342
343static void
344onSurfaceCreated_native(JNIEnv* env, jobject clazz, jint handle, jobject surface)
345{
346    if (handle != 0) {
347        NativeCode* code = (NativeCode*)handle;
348        code->setSurface(surface);
349        if (code->nativeWindow != NULL && code->callbacks.onNativeWindowCreated != NULL) {
350            code->callbacks.onNativeWindowCreated(&code->activity,
351                    code->nativeWindow.get());
352        }
353    }
354}
355
356static void
357onSurfaceChanged_native(JNIEnv* env, jobject clazz, jint handle, jobject surface,
358        jint format, jint width, jint height)
359{
360    if (handle != 0) {
361        NativeCode* code = (NativeCode*)handle;
362        sp<ANativeWindow> oldNativeWindow = code->nativeWindow;
363        code->setSurface(surface);
364        if (oldNativeWindow != code->nativeWindow) {
365            if (code->nativeWindow != NULL && code->callbacks.onNativeWindowChanged != NULL) {
366                code->callbacks.onNativeWindowChanged(&code->activity,
367                        code->nativeWindow.get());
368            }
369        }
370    }
371}
372
373static void
374onSurfaceDestroyed_native(JNIEnv* env, jobject clazz, jint handle, jobject surface)
375{
376    if (handle != 0) {
377        NativeCode* code = (NativeCode*)handle;
378        if (code->nativeWindow != NULL && code->callbacks.onNativeWindowDestroyed != NULL) {
379            code->callbacks.onNativeWindowDestroyed(&code->activity,
380                    code->nativeWindow.get());
381        }
382        code->setSurface(NULL);
383    }
384}
385
386static void
387onInputChannelCreated_native(JNIEnv* env, jobject clazz, jint handle, jobject channel)
388{
389    if (handle != 0) {
390        NativeCode* code = (NativeCode*)handle;
391        status_t err = code->setInputChannel(channel);
392        if (err != OK) {
393            jniThrowException(env, "java/lang/IllegalStateException",
394                    "Error setting input channel");
395            return;
396        }
397        if (code->callbacks.onInputQueueCreated != NULL) {
398            code->callbacks.onInputQueueCreated(&code->activity,
399                    code->nativeInputQueue);
400        }
401    }
402}
403
404static void
405onInputChannelDestroyed_native(JNIEnv* env, jobject clazz, jint handle, jobject channel)
406{
407    if (handle != 0) {
408        NativeCode* code = (NativeCode*)handle;
409        if (code->nativeInputQueue != NULL
410                && code->callbacks.onInputQueueDestroyed != NULL) {
411            code->callbacks.onInputQueueDestroyed(&code->activity,
412                    code->nativeInputQueue);
413        }
414        code->setInputChannel(NULL);
415    }
416}
417
418static const JNINativeMethod g_methods[] = {
419    { "loadNativeCode", "(Ljava/lang/String;Landroid/os/MessageQueue;)I", (void*)loadNativeCode_native },
420    { "unloadNativeCode", "(I)V", (void*)unloadNativeCode_native },
421    { "onStartNative", "(I)V", (void*)onStart_native },
422    { "onResumeNative", "(I)V", (void*)onResume_native },
423    { "onSaveInstanceStateNative", "(I)V", (void*)onSaveInstanceState_native },
424    { "onPauseNative", "(I)V", (void*)onPause_native },
425    { "onStopNative", "(I)V", (void*)onStop_native },
426    { "onLowMemoryNative", "(I)V", (void*)onLowMemory_native },
427    { "onWindowFocusChangedNative", "(IZ)V", (void*)onWindowFocusChanged_native },
428    { "onSurfaceCreatedNative", "(ILandroid/view/Surface;)V", (void*)onSurfaceCreated_native },
429    { "onSurfaceChangedNative", "(ILandroid/view/Surface;III)V", (void*)onSurfaceChanged_native },
430    { "onSurfaceDestroyedNative", "(I)V", (void*)onSurfaceDestroyed_native },
431    { "onInputChannelCreatedNative", "(ILandroid/view/InputChannel;)V", (void*)onInputChannelCreated_native },
432    { "onInputChannelDestroyedNative", "(ILandroid/view/InputChannel;)V", (void*)onInputChannelDestroyed_native },
433};
434
435static const char* const kNativeActivityPathName = "android/app/NativeActivity";
436
437#define FIND_CLASS(var, className) \
438        var = env->FindClass(className); \
439        LOG_FATAL_IF(! var, "Unable to find class " className); \
440        var = jclass(env->NewGlobalRef(var));
441
442#define GET_METHOD_ID(var, clazz, methodName, fieldDescriptor) \
443        var = env->GetMethodID(clazz, methodName, fieldDescriptor); \
444        LOG_FATAL_IF(! var, "Unable to find method" methodName);
445
446int register_android_app_NativeActivity(JNIEnv* env)
447{
448    //LOGD("register_android_app_NativeActivity");
449
450    FIND_CLASS(gNativeActivityClassInfo.clazz, kNativeActivityPathName);
451
452    GET_METHOD_ID(gNativeActivityClassInfo.dispatchUnhandledKeyEvent,
453            gNativeActivityClassInfo.clazz,
454            "dispatchUnhandledKeyEvent", "(Landroid/view/KeyEvent;)V");
455
456    GET_METHOD_ID(gNativeActivityClassInfo.setWindowFlags,
457            gNativeActivityClassInfo.clazz,
458            "setWindowFlags", "(II)V");
459    GET_METHOD_ID(gNativeActivityClassInfo.setWindowFormat,
460            gNativeActivityClassInfo.clazz,
461            "setWindowFormat", "(I)V");
462
463    return AndroidRuntime::registerNativeMethods(
464        env, kNativeActivityPathName,
465        g_methods, NELEM(g_methods));
466}
467
468} // namespace android
469