android_app_NativeActivity.cpp revision a44dd26a75e24cc021802288fb81f4761e47be6b
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#include <fcntl.h>
23
24#include <android_runtime/android_app_NativeActivity.h>
25#include <android_runtime/android_util_AssetManager.h>
26#include <android_runtime/android_view_Surface.h>
27#include <android_runtime/AndroidRuntime.h>
28#include <androidfw/InputTransport.h>
29
30#include <gui/Surface.h>
31
32#include <system/window.h>
33
34#include <utils/Looper.h>
35
36#include "JNIHelp.h"
37#include "android_os_MessageQueue.h"
38#include "android_view_InputChannel.h"
39#include "android_view_KeyEvent.h"
40
41#define LOG_TRACE(...)
42//#define LOG_TRACE(...) ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)
43
44namespace android
45{
46
47static struct {
48    jmethodID finish;
49    jmethodID setWindowFlags;
50    jmethodID setWindowFormat;
51    jmethodID showIme;
52    jmethodID hideIme;
53} gNativeActivityClassInfo;
54
55// ------------------------------------------------------------------------
56
57struct ActivityWork {
58    int32_t cmd;
59    int32_t arg1;
60    int32_t arg2;
61};
62
63enum {
64    CMD_FINISH = 1,
65    CMD_SET_WINDOW_FORMAT,
66    CMD_SET_WINDOW_FLAGS,
67    CMD_SHOW_SOFT_INPUT,
68    CMD_HIDE_SOFT_INPUT,
69};
70
71static void write_work(int fd, int32_t cmd, int32_t arg1=0, int32_t arg2=0) {
72    ActivityWork work;
73    work.cmd = cmd;
74    work.arg1 = arg1;
75    work.arg2 = arg2;
76
77    LOG_TRACE("write_work: cmd=%d", cmd);
78
79restart:
80    int res = write(fd, &work, sizeof(work));
81    if (res < 0 && errno == EINTR) {
82        goto restart;
83    }
84
85    if (res == sizeof(work)) return;
86
87    if (res < 0) ALOGW("Failed writing to work fd: %s", strerror(errno));
88    else ALOGW("Truncated writing to work fd: %d", res);
89}
90
91static bool read_work(int fd, ActivityWork* outWork) {
92    int res = read(fd, outWork, sizeof(ActivityWork));
93    // no need to worry about EINTR, poll loop will just come back again.
94    if (res == sizeof(ActivityWork)) return true;
95
96    if (res < 0) ALOGW("Failed reading work fd: %s", strerror(errno));
97    else ALOGW("Truncated reading work fd: %d", res);
98    return false;
99}
100
101/*
102 * Native state for interacting with the NativeActivity class.
103 */
104struct NativeCode : public ANativeActivity {
105    NativeCode(void* _dlhandle, ANativeActivity_createFunc* _createFunc) {
106        memset((ANativeActivity*)this, 0, sizeof(ANativeActivity));
107        memset(&callbacks, 0, sizeof(callbacks));
108        dlhandle = _dlhandle;
109        createActivityFunc = _createFunc;
110        nativeWindow = NULL;
111        mainWorkRead = mainWorkWrite = -1;
112    }
113
114    ~NativeCode() {
115        if (callbacks.onDestroy != NULL) {
116            callbacks.onDestroy(this);
117        }
118        if (env != NULL && clazz != NULL) {
119            env->DeleteGlobalRef(clazz);
120        }
121        if (messageQueue != NULL && mainWorkRead >= 0) {
122            messageQueue->getLooper()->removeFd(mainWorkRead);
123        }
124        setSurface(NULL);
125        if (mainWorkRead >= 0) close(mainWorkRead);
126        if (mainWorkWrite >= 0) close(mainWorkWrite);
127        if (dlhandle != NULL) {
128            // for now don't unload...  we probably should clean this
129            // up and only keep one open dlhandle per proc, since there
130            // is really no benefit to unloading the code.
131            //dlclose(dlhandle);
132        }
133    }
134
135    void setSurface(jobject _surface) {
136        if (_surface != NULL) {
137            nativeWindow = android_view_Surface_getNativeWindow(env, _surface);
138        } else {
139            nativeWindow = NULL;
140        }
141    }
142
143    ANativeActivityCallbacks callbacks;
144
145    void* dlhandle;
146    ANativeActivity_createFunc* createActivityFunc;
147
148    String8 internalDataPathObj;
149    String8 externalDataPathObj;
150    String8 obbPathObj;
151
152    sp<ANativeWindow> nativeWindow;
153    int32_t lastWindowWidth;
154    int32_t lastWindowHeight;
155
156    // These are used to wake up the main thread to process work.
157    int mainWorkRead;
158    int mainWorkWrite;
159    sp<MessageQueue> messageQueue;
160};
161
162void android_NativeActivity_finish(ANativeActivity* activity) {
163    NativeCode* code = static_cast<NativeCode*>(activity);
164    write_work(code->mainWorkWrite, CMD_FINISH, 0);
165}
166
167void android_NativeActivity_setWindowFormat(
168        ANativeActivity* activity, int32_t format) {
169    NativeCode* code = static_cast<NativeCode*>(activity);
170    write_work(code->mainWorkWrite, CMD_SET_WINDOW_FORMAT, format);
171}
172
173void android_NativeActivity_setWindowFlags(
174        ANativeActivity* activity, int32_t values, int32_t mask) {
175    NativeCode* code = static_cast<NativeCode*>(activity);
176    write_work(code->mainWorkWrite, CMD_SET_WINDOW_FLAGS, values, mask);
177}
178
179void android_NativeActivity_showSoftInput(
180        ANativeActivity* activity, int32_t flags) {
181    NativeCode* code = static_cast<NativeCode*>(activity);
182    write_work(code->mainWorkWrite, CMD_SHOW_SOFT_INPUT, flags);
183}
184
185void android_NativeActivity_hideSoftInput(
186        ANativeActivity* activity, int32_t flags) {
187    NativeCode* code = static_cast<NativeCode*>(activity);
188    write_work(code->mainWorkWrite, CMD_HIDE_SOFT_INPUT, flags);
189}
190
191// ------------------------------------------------------------------------
192
193/*
194 * Callback for handling native events on the application's main thread.
195 */
196static int mainWorkCallback(int fd, int events, void* data) {
197    NativeCode* code = (NativeCode*)data;
198    if ((events & POLLIN) == 0) {
199        return 1;
200    }
201
202    ActivityWork work;
203    if (!read_work(code->mainWorkRead, &work)) {
204        return 1;
205    }
206
207    LOG_TRACE("mainWorkCallback: cmd=%d", work.cmd);
208
209    switch (work.cmd) {
210        case CMD_FINISH: {
211            code->env->CallVoidMethod(code->clazz, gNativeActivityClassInfo.finish);
212            code->messageQueue->raiseAndClearException(code->env, "finish");
213        } break;
214        case CMD_SET_WINDOW_FORMAT: {
215            code->env->CallVoidMethod(code->clazz,
216                    gNativeActivityClassInfo.setWindowFormat, work.arg1);
217            code->messageQueue->raiseAndClearException(code->env, "setWindowFormat");
218        } break;
219        case CMD_SET_WINDOW_FLAGS: {
220            code->env->CallVoidMethod(code->clazz,
221                    gNativeActivityClassInfo.setWindowFlags, work.arg1, work.arg2);
222            code->messageQueue->raiseAndClearException(code->env, "setWindowFlags");
223        } break;
224        case CMD_SHOW_SOFT_INPUT: {
225            code->env->CallVoidMethod(code->clazz,
226                    gNativeActivityClassInfo.showIme, work.arg1);
227            code->messageQueue->raiseAndClearException(code->env, "showIme");
228        } break;
229        case CMD_HIDE_SOFT_INPUT: {
230            code->env->CallVoidMethod(code->clazz,
231                    gNativeActivityClassInfo.hideIme, work.arg1);
232            code->messageQueue->raiseAndClearException(code->env, "hideIme");
233        } break;
234        default:
235            ALOGW("Unknown work command: %d", work.cmd);
236            break;
237    }
238
239    return 1;
240}
241
242// ------------------------------------------------------------------------
243
244static jint
245loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path, jstring funcName,
246        jobject messageQueue, jstring internalDataDir, jstring obbDir,
247        jstring externalDataDir, int sdkVersion,
248        jobject jAssetMgr, jbyteArray savedState)
249{
250    LOG_TRACE("loadNativeCode_native");
251
252    const char* pathStr = env->GetStringUTFChars(path, NULL);
253    NativeCode* code = NULL;
254
255    void* handle = dlopen(pathStr, RTLD_LAZY);
256
257    env->ReleaseStringUTFChars(path, pathStr);
258
259    if (handle != NULL) {
260        const char* funcStr = env->GetStringUTFChars(funcName, NULL);
261        code = new NativeCode(handle, (ANativeActivity_createFunc*)
262                dlsym(handle, funcStr));
263        env->ReleaseStringUTFChars(funcName, funcStr);
264
265        if (code->createActivityFunc == NULL) {
266            ALOGW("ANativeActivity_onCreate not found");
267            delete code;
268            return 0;
269        }
270
271        code->messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueue);
272        if (code->messageQueue == NULL) {
273            ALOGW("Unable to retrieve native MessageQueue");
274            delete code;
275            return 0;
276        }
277
278        int msgpipe[2];
279        if (pipe(msgpipe)) {
280            ALOGW("could not create pipe: %s", strerror(errno));
281            delete code;
282            return 0;
283        }
284        code->mainWorkRead = msgpipe[0];
285        code->mainWorkWrite = msgpipe[1];
286        int result = fcntl(code->mainWorkRead, F_SETFL, O_NONBLOCK);
287        SLOGW_IF(result != 0, "Could not make main work read pipe "
288                "non-blocking: %s", strerror(errno));
289        result = fcntl(code->mainWorkWrite, F_SETFL, O_NONBLOCK);
290        SLOGW_IF(result != 0, "Could not make main work write pipe "
291                "non-blocking: %s", strerror(errno));
292        code->messageQueue->getLooper()->addFd(
293                code->mainWorkRead, 0, ALOOPER_EVENT_INPUT, mainWorkCallback, code);
294
295        code->ANativeActivity::callbacks = &code->callbacks;
296        if (env->GetJavaVM(&code->vm) < 0) {
297            ALOGW("NativeActivity GetJavaVM failed");
298            delete code;
299            return 0;
300        }
301        code->env = env;
302        code->clazz = env->NewGlobalRef(clazz);
303
304        const char* dirStr = env->GetStringUTFChars(internalDataDir, NULL);
305        code->internalDataPathObj = dirStr;
306        code->internalDataPath = code->internalDataPathObj.string();
307        env->ReleaseStringUTFChars(internalDataDir, dirStr);
308
309        dirStr = env->GetStringUTFChars(externalDataDir, NULL);
310        code->externalDataPathObj = dirStr;
311        code->externalDataPath = code->externalDataPathObj.string();
312        env->ReleaseStringUTFChars(externalDataDir, dirStr);
313
314        code->sdkVersion = sdkVersion;
315
316        code->assetManager = assetManagerForJavaObject(env, jAssetMgr);
317
318        dirStr = env->GetStringUTFChars(obbDir, NULL);
319        code->obbPathObj = dirStr;
320        code->obbPath = code->obbPathObj.string();
321        env->ReleaseStringUTFChars(obbDir, dirStr);
322
323        jbyte* rawSavedState = NULL;
324        jsize rawSavedSize = 0;
325        if (savedState != NULL) {
326            rawSavedState = env->GetByteArrayElements(savedState, NULL);
327            rawSavedSize = env->GetArrayLength(savedState);
328        }
329
330        code->createActivityFunc(code, rawSavedState, rawSavedSize);
331
332        if (rawSavedState != NULL) {
333            env->ReleaseByteArrayElements(savedState, rawSavedState, 0);
334        }
335    }
336
337    return (jint)code;
338}
339
340static void
341unloadNativeCode_native(JNIEnv* env, jobject clazz, jint handle)
342{
343    LOG_TRACE("unloadNativeCode_native");
344    if (handle != 0) {
345        NativeCode* code = (NativeCode*)handle;
346        delete code;
347    }
348}
349
350static void
351onStart_native(JNIEnv* env, jobject clazz, jint handle)
352{
353    LOG_TRACE("onStart_native");
354    if (handle != 0) {
355        NativeCode* code = (NativeCode*)handle;
356        if (code->callbacks.onStart != NULL) {
357            code->callbacks.onStart(code);
358        }
359    }
360}
361
362static void
363onResume_native(JNIEnv* env, jobject clazz, jint handle)
364{
365    LOG_TRACE("onResume_native");
366    if (handle != 0) {
367        NativeCode* code = (NativeCode*)handle;
368        if (code->callbacks.onResume != NULL) {
369            code->callbacks.onResume(code);
370        }
371    }
372}
373
374static jbyteArray
375onSaveInstanceState_native(JNIEnv* env, jobject clazz, jint handle)
376{
377    LOG_TRACE("onSaveInstanceState_native");
378
379    jbyteArray array = NULL;
380
381    if (handle != 0) {
382        NativeCode* code = (NativeCode*)handle;
383        if (code->callbacks.onSaveInstanceState != NULL) {
384            size_t len = 0;
385            jbyte* state = (jbyte*)code->callbacks.onSaveInstanceState(code, &len);
386            if (len > 0) {
387                array = env->NewByteArray(len);
388                if (array != NULL) {
389                    env->SetByteArrayRegion(array, 0, len, state);
390                }
391            }
392            if (state != NULL) {
393                free(state);
394            }
395        }
396    }
397
398    return array;
399}
400
401static void
402onPause_native(JNIEnv* env, jobject clazz, jint handle)
403{
404    LOG_TRACE("onPause_native");
405    if (handle != 0) {
406        NativeCode* code = (NativeCode*)handle;
407        if (code->callbacks.onPause != NULL) {
408            code->callbacks.onPause(code);
409        }
410    }
411}
412
413static void
414onStop_native(JNIEnv* env, jobject clazz, jint handle)
415{
416    LOG_TRACE("onStop_native");
417    if (handle != 0) {
418        NativeCode* code = (NativeCode*)handle;
419        if (code->callbacks.onStop != NULL) {
420            code->callbacks.onStop(code);
421        }
422    }
423}
424
425static void
426onConfigurationChanged_native(JNIEnv* env, jobject clazz, jint handle)
427{
428    LOG_TRACE("onConfigurationChanged_native");
429    if (handle != 0) {
430        NativeCode* code = (NativeCode*)handle;
431        if (code->callbacks.onConfigurationChanged != NULL) {
432            code->callbacks.onConfigurationChanged(code);
433        }
434    }
435}
436
437static void
438onLowMemory_native(JNIEnv* env, jobject clazz, jint handle)
439{
440    LOG_TRACE("onLowMemory_native");
441    if (handle != 0) {
442        NativeCode* code = (NativeCode*)handle;
443        if (code->callbacks.onLowMemory != NULL) {
444            code->callbacks.onLowMemory(code);
445        }
446    }
447}
448
449static void
450onWindowFocusChanged_native(JNIEnv* env, jobject clazz, jint handle, jboolean focused)
451{
452    LOG_TRACE("onWindowFocusChanged_native");
453    if (handle != 0) {
454        NativeCode* code = (NativeCode*)handle;
455        if (code->callbacks.onWindowFocusChanged != NULL) {
456            code->callbacks.onWindowFocusChanged(code, focused ? 1 : 0);
457        }
458    }
459}
460
461static void
462onSurfaceCreated_native(JNIEnv* env, jobject clazz, jint handle, jobject surface)
463{
464    LOG_TRACE("onSurfaceCreated_native");
465    if (handle != 0) {
466        NativeCode* code = (NativeCode*)handle;
467        code->setSurface(surface);
468        if (code->nativeWindow != NULL && code->callbacks.onNativeWindowCreated != NULL) {
469            code->callbacks.onNativeWindowCreated(code,
470                    code->nativeWindow.get());
471        }
472    }
473}
474
475static int32_t getWindowProp(ANativeWindow* window, int what) {
476    int value;
477    int res = window->query(window, what, &value);
478    return res < 0 ? res : value;
479}
480
481static void
482onSurfaceChanged_native(JNIEnv* env, jobject clazz, jint handle, jobject surface,
483        jint format, jint width, jint height)
484{
485    LOG_TRACE("onSurfaceChanged_native");
486    if (handle != 0) {
487        NativeCode* code = (NativeCode*)handle;
488        sp<ANativeWindow> oldNativeWindow = code->nativeWindow;
489        code->setSurface(surface);
490        if (oldNativeWindow != code->nativeWindow) {
491            if (oldNativeWindow != NULL && code->callbacks.onNativeWindowDestroyed != NULL) {
492                code->callbacks.onNativeWindowDestroyed(code,
493                        oldNativeWindow.get());
494            }
495            if (code->nativeWindow != NULL) {
496                if (code->callbacks.onNativeWindowCreated != NULL) {
497                    code->callbacks.onNativeWindowCreated(code,
498                            code->nativeWindow.get());
499                }
500                code->lastWindowWidth = getWindowProp(code->nativeWindow.get(),
501                        NATIVE_WINDOW_WIDTH);
502                code->lastWindowHeight = getWindowProp(code->nativeWindow.get(),
503                        NATIVE_WINDOW_HEIGHT);
504            }
505        } else {
506            // Maybe it resized?
507            int32_t newWidth = getWindowProp(code->nativeWindow.get(),
508                    NATIVE_WINDOW_WIDTH);
509            int32_t newHeight = getWindowProp(code->nativeWindow.get(),
510                    NATIVE_WINDOW_HEIGHT);
511            if (newWidth != code->lastWindowWidth
512                    || newHeight != code->lastWindowHeight) {
513                if (code->callbacks.onNativeWindowResized != NULL) {
514                    code->callbacks.onNativeWindowResized(code,
515                            code->nativeWindow.get());
516                }
517            }
518        }
519    }
520}
521
522static void
523onSurfaceRedrawNeeded_native(JNIEnv* env, jobject clazz, jint handle)
524{
525    LOG_TRACE("onSurfaceRedrawNeeded_native");
526    if (handle != 0) {
527        NativeCode* code = (NativeCode*)handle;
528        if (code->nativeWindow != NULL && code->callbacks.onNativeWindowRedrawNeeded != NULL) {
529            code->callbacks.onNativeWindowRedrawNeeded(code, code->nativeWindow.get());
530        }
531    }
532}
533
534static void
535onSurfaceDestroyed_native(JNIEnv* env, jobject clazz, jint handle, jobject surface)
536{
537    LOG_TRACE("onSurfaceDestroyed_native");
538    if (handle != 0) {
539        NativeCode* code = (NativeCode*)handle;
540        if (code->nativeWindow != NULL && code->callbacks.onNativeWindowDestroyed != NULL) {
541            code->callbacks.onNativeWindowDestroyed(code,
542                    code->nativeWindow.get());
543        }
544        code->setSurface(NULL);
545    }
546}
547
548static void
549onInputQueueCreated_native(JNIEnv* env, jobject clazz, jint handle, jint queuePtr)
550{
551    LOG_TRACE("onInputChannelCreated_native");
552    if (handle != 0) {
553        NativeCode* code = (NativeCode*)handle;
554        if (code->callbacks.onInputQueueCreated != NULL) {
555            AInputQueue* queue = reinterpret_cast<AInputQueue*>(queuePtr);
556            code->callbacks.onInputQueueCreated(code, queue);
557        }
558    }
559}
560
561static void
562onInputQueueDestroyed_native(JNIEnv* env, jobject clazz, jint handle, jint queuePtr)
563{
564    LOG_TRACE("onInputChannelDestroyed_native");
565    if (handle != 0) {
566        NativeCode* code = (NativeCode*)handle;
567        if (code->callbacks.onInputQueueDestroyed != NULL) {
568            AInputQueue* queue = reinterpret_cast<AInputQueue*>(queuePtr);
569            code->callbacks.onInputQueueDestroyed(code, queue);
570        }
571    }
572}
573
574static void
575onContentRectChanged_native(JNIEnv* env, jobject clazz, jint handle,
576        jint x, jint y, jint w, jint h)
577{
578    LOG_TRACE("onContentRectChanged_native");
579    if (handle != 0) {
580        NativeCode* code = (NativeCode*)handle;
581        if (code->callbacks.onContentRectChanged != NULL) {
582            ARect rect;
583            rect.left = x;
584            rect.top = y;
585            rect.right = x+w;
586            rect.bottom = y+h;
587            code->callbacks.onContentRectChanged(code, &rect);
588        }
589    }
590}
591
592static const JNINativeMethod g_methods[] = {
593    { "loadNativeCode", "(Ljava/lang/String;Ljava/lang/String;Landroid/os/MessageQueue;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILandroid/content/res/AssetManager;[B)I",
594            (void*)loadNativeCode_native },
595    { "unloadNativeCode", "(I)V", (void*)unloadNativeCode_native },
596    { "onStartNative", "(I)V", (void*)onStart_native },
597    { "onResumeNative", "(I)V", (void*)onResume_native },
598    { "onSaveInstanceStateNative", "(I)[B", (void*)onSaveInstanceState_native },
599    { "onPauseNative", "(I)V", (void*)onPause_native },
600    { "onStopNative", "(I)V", (void*)onStop_native },
601    { "onConfigurationChangedNative", "(I)V", (void*)onConfigurationChanged_native },
602    { "onLowMemoryNative", "(I)V", (void*)onLowMemory_native },
603    { "onWindowFocusChangedNative", "(IZ)V", (void*)onWindowFocusChanged_native },
604    { "onSurfaceCreatedNative", "(ILandroid/view/Surface;)V", (void*)onSurfaceCreated_native },
605    { "onSurfaceChangedNative", "(ILandroid/view/Surface;III)V", (void*)onSurfaceChanged_native },
606    { "onSurfaceRedrawNeededNative", "(ILandroid/view/Surface;)V", (void*)onSurfaceRedrawNeeded_native },
607    { "onSurfaceDestroyedNative", "(I)V", (void*)onSurfaceDestroyed_native },
608    { "onInputQueueCreatedNative", "(II)V",
609        (void*)onInputQueueCreated_native },
610    { "onInputQueueDestroyedNative", "(II)V",
611        (void*)onInputQueueDestroyed_native },
612    { "onContentRectChangedNative", "(IIIII)V", (void*)onContentRectChanged_native },
613};
614
615static const char* const kNativeActivityPathName = "android/app/NativeActivity";
616
617#define FIND_CLASS(var, className) \
618        var = env->FindClass(className); \
619        LOG_FATAL_IF(! var, "Unable to find class %s", className);
620
621#define GET_METHOD_ID(var, clazz, methodName, fieldDescriptor) \
622        var = env->GetMethodID(clazz, methodName, fieldDescriptor); \
623        LOG_FATAL_IF(! var, "Unable to find method" methodName);
624
625int register_android_app_NativeActivity(JNIEnv* env)
626{
627    //ALOGD("register_android_app_NativeActivity");
628    jclass clazz;
629    FIND_CLASS(clazz, kNativeActivityPathName);
630
631    GET_METHOD_ID(gNativeActivityClassInfo.finish,
632            clazz,
633            "finish", "()V");
634    GET_METHOD_ID(gNativeActivityClassInfo.setWindowFlags,
635            clazz,
636            "setWindowFlags", "(II)V");
637    GET_METHOD_ID(gNativeActivityClassInfo.setWindowFormat,
638            clazz,
639            "setWindowFormat", "(I)V");
640    GET_METHOD_ID(gNativeActivityClassInfo.showIme,
641            clazz,
642            "showIme", "(I)V");
643    GET_METHOD_ID(gNativeActivityClassInfo.hideIme,
644            clazz,
645            "hideIme", "(I)V");
646
647    return AndroidRuntime::registerNativeMethods(
648        env, kNativeActivityPathName,
649        g_methods, NELEM(g_methods));
650}
651
652} // namespace android
653