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