android_app_NativeActivity.cpp revision 805fd7ee0e5dc2939e85c84f78d9890a51982bc0
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/AndroidRuntime.h>
25#include <android_runtime/android_view_Surface.h>
26#include <android_runtime/android_app_NativeActivity.h>
27#include <android_runtime/android_util_AssetManager.h>
28#include <surfaceflinger/Surface.h>
29#include <ui/egl/android_natives.h>
30#include <ui/InputTransport.h>
31#include <utils/Looper.h>
32
33#include "JNIHelp.h"
34#include "android_os_MessageQueue.h"
35#include "android_view_InputChannel.h"
36#include "android_view_KeyEvent.h"
37
38#define LOG_TRACE(...)
39//#define LOG_TRACE(...) LOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)
40
41namespace android
42{
43
44static struct {
45    jclass clazz;
46
47    jmethodID dispatchUnhandledKeyEvent;
48    jmethodID preDispatchKeyEvent;
49    jmethodID finish;
50    jmethodID setWindowFlags;
51    jmethodID setWindowFormat;
52    jmethodID showIme;
53    jmethodID hideIme;
54} gNativeActivityClassInfo;
55
56// ------------------------------------------------------------------------
57
58struct ActivityWork {
59    int32_t cmd;
60    int32_t arg1;
61    int32_t arg2;
62};
63
64enum {
65    CMD_DEF_KEY = 1,
66    CMD_FINISH,
67    CMD_SET_WINDOW_FORMAT,
68    CMD_SET_WINDOW_FLAGS,
69    CMD_SHOW_SOFT_INPUT,
70    CMD_HIDE_SOFT_INPUT,
71};
72
73static void write_work(int fd, int32_t cmd, int32_t arg1=0, int32_t arg2=0) {
74    ActivityWork work;
75    work.cmd = cmd;
76    work.arg1 = arg1;
77    work.arg2 = arg2;
78
79    LOG_TRACE("write_work: cmd=%d", cmd);
80
81restart:
82    int res = write(fd, &work, sizeof(work));
83    if (res < 0 && errno == EINTR) {
84        goto restart;
85    }
86
87    if (res == sizeof(work)) return;
88
89    if (res < 0) LOGW("Failed writing to work fd: %s", strerror(errno));
90    else LOGW("Truncated writing to work fd: %d", res);
91}
92
93static bool read_work(int fd, ActivityWork* outWork) {
94    int res = read(fd, outWork, sizeof(ActivityWork));
95    // no need to worry about EINTR, poll loop will just come back again.
96    if (res == sizeof(ActivityWork)) return true;
97
98    if (res < 0) LOGW("Failed reading work fd: %s", strerror(errno));
99    else LOGW("Truncated reading work fd: %d", res);
100    return false;
101}
102
103// ------------------------------------------------------------------------
104
105} // namespace android
106
107using namespace android;
108
109AInputQueue::AInputQueue(const sp<InputChannel>& channel, int workWrite) :
110        mWorkWrite(workWrite), mConsumer(channel), mSeq(0) {
111    int msgpipe[2];
112    if (pipe(msgpipe)) {
113        LOGW("could not create pipe: %s", strerror(errno));
114        mDispatchKeyRead = mDispatchKeyWrite = -1;
115    } else {
116        mDispatchKeyRead = msgpipe[0];
117        mDispatchKeyWrite = msgpipe[1];
118        int result = fcntl(mDispatchKeyRead, F_SETFL, O_NONBLOCK);
119        SLOGW_IF(result != 0, "Could not make AInputQueue read pipe "
120                "non-blocking: %s", strerror(errno));
121        result = fcntl(mDispatchKeyWrite, F_SETFL, O_NONBLOCK);
122        SLOGW_IF(result != 0, "Could not make AInputQueue write pipe "
123                "non-blocking: %s", strerror(errno));
124    }
125}
126
127AInputQueue::~AInputQueue() {
128    close(mDispatchKeyRead);
129    close(mDispatchKeyWrite);
130}
131
132void AInputQueue::attachLooper(ALooper* looper, int ident,
133        ALooper_callbackFunc callback, void* data) {
134    mLooper = static_cast<android::Looper*>(looper);
135    mLooper->addFd(mConsumer.getChannel()->getReceivePipeFd(),
136            ident, ALOOPER_EVENT_INPUT, callback, data);
137    mLooper->addFd(mDispatchKeyRead,
138            ident, ALOOPER_EVENT_INPUT, callback, data);
139}
140
141void AInputQueue::detachLooper() {
142    mLooper->removeFd(mConsumer.getChannel()->getReceivePipeFd());
143    mLooper->removeFd(mDispatchKeyRead);
144}
145
146int32_t AInputQueue::hasEvents() {
147    struct pollfd pfd[2];
148
149    pfd[0].fd = mConsumer.getChannel()->getReceivePipeFd();
150    pfd[0].events = POLLIN;
151    pfd[0].revents = 0;
152    pfd[1].fd = mDispatchKeyRead;
153    pfd[0].events = POLLIN;
154    pfd[0].revents = 0;
155
156    int nfd = poll(pfd, 2, 0);
157    if (nfd <= 0) return 0;
158    return (pfd[0].revents == POLLIN || pfd[1].revents == POLLIN) ? 1 : -1;
159}
160
161int32_t AInputQueue::getEvent(AInputEvent** outEvent) {
162    *outEvent = NULL;
163
164    bool finishNow = false;
165
166    char byteread;
167    ssize_t nRead = read(mDispatchKeyRead, &byteread, 1);
168    if (nRead == 1) {
169        mLock.lock();
170        if (mDispatchingKeys.size() > 0) {
171            KeyEvent* kevent = mDispatchingKeys[0];
172            *outEvent = kevent;
173            mDispatchingKeys.removeAt(0);
174            in_flight_event inflight;
175            inflight.event = kevent;
176            inflight.seq = -1;
177            inflight.doFinish = false;
178            mInFlightEvents.push(inflight);
179        }
180        if (mFinishPreDispatches.size() > 0) {
181            finish_pre_dispatch finish(mFinishPreDispatches[0]);
182            mFinishPreDispatches.removeAt(0);
183            const size_t N = mInFlightEvents.size();
184            for (size_t i=0; i<N; i++) {
185                const in_flight_event& inflight(mInFlightEvents[i]);
186                if (inflight.seq == finish.seq) {
187                    *outEvent = inflight.event;
188                    finishNow = finish.handled;
189                }
190            }
191            if (*outEvent == NULL) {
192                LOGW("getEvent couldn't find inflight for seq %d", finish.seq);
193            }
194        }
195        mLock.unlock();
196
197        if (finishNow) {
198            finishEvent(*outEvent, true, false);
199            *outEvent = NULL;
200            return -1;
201        } else if (*outEvent != NULL) {
202            return 0;
203        }
204    }
205
206    int32_t res = mConsumer.receiveDispatchSignal();
207    if (res != android::OK) {
208        LOGE("channel '%s' ~ Failed to receive dispatch signal.  status=%d",
209                mConsumer.getChannel()->getName().string(), res);
210        return -1;
211    }
212
213    InputEvent* myEvent = NULL;
214    res = mConsumer.consume(this, &myEvent);
215    if (res != android::OK) {
216        LOGW("channel '%s' ~ Failed to consume input event.  status=%d",
217                mConsumer.getChannel()->getName().string(), res);
218        mConsumer.sendFinishedSignal(false);
219        return -1;
220    }
221
222    in_flight_event inflight;
223    inflight.event = myEvent;
224    inflight.seq = -1;
225    inflight.doFinish = true;
226    mInFlightEvents.push(inflight);
227
228    *outEvent = myEvent;
229    return 0;
230}
231
232bool AInputQueue::preDispatchEvent(AInputEvent* event) {
233    if (((InputEvent*)event)->getType() != AINPUT_EVENT_TYPE_KEY) {
234        // The IME only cares about key events.
235        return false;
236    }
237
238    // For now we only send system keys to the IME...  this avoids having
239    // critical keys like DPAD go through this path.  We really need to have
240    // the IME report which keys it wants.
241    if (!((KeyEvent*)event)->isSystemKey()) {
242        return false;
243    }
244
245    return preDispatchKey((KeyEvent*)event);
246}
247
248void AInputQueue::finishEvent(AInputEvent* event, bool handled, bool didDefaultHandling) {
249    LOG_TRACE("finishEvent: %p handled=%d, didDefaultHandling=%d", event,
250            handled ? 1 : 0, didDefaultHandling ? 1 : 0);
251
252    if (!handled && !didDefaultHandling
253            && ((InputEvent*)event)->getType() == AINPUT_EVENT_TYPE_KEY
254            && ((KeyEvent*)event)->hasDefaultAction()) {
255        // The app didn't handle this, but it may have a default action
256        // associated with it.  We need to hand this back to Java to be
257        // executed.
258        doUnhandledKey((KeyEvent*)event);
259        return;
260    }
261
262    mLock.lock();
263    const size_t N = mInFlightEvents.size();
264    for (size_t i=0; i<N; i++) {
265        const in_flight_event& inflight(mInFlightEvents[i]);
266        if (inflight.event == event) {
267            if (inflight.doFinish) {
268                int32_t res = mConsumer.sendFinishedSignal(handled);
269                if (res != android::OK) {
270                    LOGW("Failed to send finished signal on channel '%s'.  status=%d",
271                            mConsumer.getChannel()->getName().string(), res);
272                }
273            }
274            if (static_cast<InputEvent*>(event)->getType() == AINPUT_EVENT_TYPE_KEY) {
275                mAvailKeyEvents.push(static_cast<KeyEvent*>(event));
276            } else {
277                mAvailMotionEvents.push(static_cast<MotionEvent*>(event));
278            }
279            mInFlightEvents.removeAt(i);
280            mLock.unlock();
281            return;
282        }
283    }
284    mLock.unlock();
285
286    LOGW("finishEvent called for unknown event: %p", event);
287}
288
289void AInputQueue::dispatchEvent(android::KeyEvent* event) {
290    mLock.lock();
291    LOG_TRACE("dispatchEvent: dispatching=%d write=%d\n", mDispatchingKeys.size(),
292            mDispatchKeyWrite);
293    mDispatchingKeys.add(event);
294    wakeupDispatch();
295    mLock.unlock();
296}
297
298void AInputQueue::finishPreDispatch(int seq, bool handled) {
299    mLock.lock();
300    LOG_TRACE("finishPreDispatch: seq=%d handled=%d\n", seq, handled ? 1 : 0);
301    finish_pre_dispatch finish;
302    finish.seq = seq;
303    finish.handled = handled;
304    mFinishPreDispatches.add(finish);
305    wakeupDispatch();
306    mLock.unlock();
307}
308
309KeyEvent* AInputQueue::consumeUnhandledEvent() {
310    KeyEvent* event = NULL;
311
312    mLock.lock();
313    if (mUnhandledKeys.size() > 0) {
314        event = mUnhandledKeys[0];
315        mUnhandledKeys.removeAt(0);
316    }
317    mLock.unlock();
318
319    LOG_TRACE("consumeUnhandledEvent: KeyEvent=%p", event);
320
321    return event;
322}
323
324KeyEvent* AInputQueue::consumePreDispatchingEvent(int* outSeq) {
325    KeyEvent* event = NULL;
326
327    mLock.lock();
328    if (mPreDispatchingKeys.size() > 0) {
329        const in_flight_event& inflight(mPreDispatchingKeys[0]);
330        event = static_cast<KeyEvent*>(inflight.event);
331        *outSeq = inflight.seq;
332        mPreDispatchingKeys.removeAt(0);
333    }
334    mLock.unlock();
335
336    LOG_TRACE("consumePreDispatchingEvent: KeyEvent=%p", event);
337
338    return event;
339}
340
341KeyEvent* AInputQueue::createKeyEvent() {
342    mLock.lock();
343    KeyEvent* event;
344    if (mAvailKeyEvents.size() <= 0) {
345        event = new KeyEvent();
346    } else {
347        event = mAvailKeyEvents.top();
348        mAvailKeyEvents.pop();
349    }
350    mLock.unlock();
351    return event;
352}
353
354MotionEvent* AInputQueue::createMotionEvent() {
355    mLock.lock();
356    MotionEvent* event;
357    if (mAvailMotionEvents.size() <= 0) {
358        event = new MotionEvent();
359    } else {
360        event = mAvailMotionEvents.top();
361        mAvailMotionEvents.pop();
362    }
363    mLock.unlock();
364    return event;
365}
366
367void AInputQueue::doUnhandledKey(KeyEvent* keyEvent) {
368    mLock.lock();
369    LOG_TRACE("Unhandled key: pending=%d write=%d\n", mUnhandledKeys.size(), mWorkWrite);
370    if (mUnhandledKeys.size() <= 0 && mWorkWrite >= 0) {
371        write_work(mWorkWrite, CMD_DEF_KEY);
372    }
373    mUnhandledKeys.add(keyEvent);
374    mLock.unlock();
375}
376
377bool AInputQueue::preDispatchKey(KeyEvent* keyEvent) {
378    mLock.lock();
379    LOG_TRACE("preDispatch key: pending=%d write=%d\n", mPreDispatchingKeys.size(), mWorkWrite);
380    const size_t N = mInFlightEvents.size();
381    for (size_t i=0; i<N; i++) {
382        in_flight_event& inflight(mInFlightEvents.editItemAt(i));
383        if (inflight.event == keyEvent) {
384            if (inflight.seq >= 0) {
385                // This event has already been pre-dispatched!
386                LOG_TRACE("Event already pre-dispatched!");
387                mLock.unlock();
388                return false;
389            }
390            mSeq++;
391            if (mSeq < 0) mSeq = 1;
392            inflight.seq = mSeq;
393
394            if (mPreDispatchingKeys.size() <= 0 && mWorkWrite >= 0) {
395                write_work(mWorkWrite, CMD_DEF_KEY);
396            }
397            mPreDispatchingKeys.add(inflight);
398            mLock.unlock();
399            return true;
400        }
401    }
402
403    LOGW("preDispatchKey called for unknown event: %p", keyEvent);
404    return false;
405}
406
407void AInputQueue::wakeupDispatch() {
408restart:
409    char dummy = 0;
410    int res = write(mDispatchKeyWrite, &dummy, sizeof(dummy));
411    if (res < 0 && errno == EINTR) {
412        goto restart;
413    }
414
415    if (res == sizeof(dummy)) return;
416
417    if (res < 0) LOGW("Failed writing to dispatch fd: %s", strerror(errno));
418    else LOGW("Truncated writing to dispatch fd: %d", res);
419}
420
421namespace android {
422
423// ------------------------------------------------------------------------
424
425/*
426 * Native state for interacting with the NativeActivity class.
427 */
428struct NativeCode : public ANativeActivity {
429    NativeCode(void* _dlhandle, ANativeActivity_createFunc* _createFunc) {
430        memset((ANativeActivity*)this, 0, sizeof(ANativeActivity));
431        memset(&callbacks, 0, sizeof(callbacks));
432        dlhandle = _dlhandle;
433        createActivityFunc = _createFunc;
434        nativeWindow = NULL;
435        inputChannel = NULL;
436        nativeInputQueue = NULL;
437        mainWorkRead = mainWorkWrite = -1;
438    }
439
440    ~NativeCode() {
441        if (callbacks.onDestroy != NULL) {
442            callbacks.onDestroy(this);
443        }
444        if (env != NULL && clazz != NULL) {
445            env->DeleteGlobalRef(clazz);
446        }
447        if (looper != NULL && mainWorkRead >= 0) {
448            looper->removeFd(mainWorkRead);
449        }
450        if (nativeInputQueue != NULL) {
451            nativeInputQueue->mWorkWrite = -1;
452        }
453        setSurface(NULL);
454        setInputChannel(NULL);
455        if (mainWorkRead >= 0) close(mainWorkRead);
456        if (mainWorkWrite >= 0) close(mainWorkWrite);
457        if (dlhandle != NULL) {
458            // for now don't unload...  we probably should clean this
459            // up and only keep one open dlhandle per proc, since there
460            // is really no benefit to unloading the code.
461            //dlclose(dlhandle);
462        }
463    }
464
465    void setSurface(jobject _surface) {
466        if (_surface != NULL) {
467            nativeWindow = android_Surface_getNativeWindow(env, _surface);
468        } else {
469            nativeWindow = NULL;
470        }
471    }
472
473    status_t setInputChannel(jobject _channel) {
474        if (inputChannel != NULL) {
475            delete nativeInputQueue;
476            env->DeleteGlobalRef(inputChannel);
477        }
478        inputChannel = NULL;
479        nativeInputQueue = NULL;
480        if (_channel != NULL) {
481            inputChannel = env->NewGlobalRef(_channel);
482            sp<InputChannel> ic =
483                    android_view_InputChannel_getInputChannel(env, _channel);
484            if (ic != NULL) {
485                nativeInputQueue = new AInputQueue(ic, mainWorkWrite);
486                if (nativeInputQueue->getConsumer().initialize() != android::OK) {
487                    delete nativeInputQueue;
488                    nativeInputQueue = NULL;
489                    return UNKNOWN_ERROR;
490                }
491            } else {
492                return UNKNOWN_ERROR;
493            }
494        }
495        return OK;
496    }
497
498    ANativeActivityCallbacks callbacks;
499
500    void* dlhandle;
501    ANativeActivity_createFunc* createActivityFunc;
502
503    String8 internalDataPathObj;
504    String8 externalDataPathObj;
505    String8 obbPathObj;
506
507    sp<ANativeWindow> nativeWindow;
508    int32_t lastWindowWidth;
509    int32_t lastWindowHeight;
510
511    jobject inputChannel;
512    struct AInputQueue* nativeInputQueue;
513
514    // These are used to wake up the main thread to process work.
515    int mainWorkRead;
516    int mainWorkWrite;
517    sp<Looper> looper;
518};
519
520void android_NativeActivity_finish(ANativeActivity* activity) {
521    NativeCode* code = static_cast<NativeCode*>(activity);
522    write_work(code->mainWorkWrite, CMD_FINISH, 0);
523}
524
525void android_NativeActivity_setWindowFormat(
526        ANativeActivity* activity, int32_t format) {
527    NativeCode* code = static_cast<NativeCode*>(activity);
528    write_work(code->mainWorkWrite, CMD_SET_WINDOW_FORMAT, format);
529}
530
531void android_NativeActivity_setWindowFlags(
532        ANativeActivity* activity, int32_t values, int32_t mask) {
533    NativeCode* code = static_cast<NativeCode*>(activity);
534    write_work(code->mainWorkWrite, CMD_SET_WINDOW_FLAGS, values, mask);
535}
536
537void android_NativeActivity_showSoftInput(
538        ANativeActivity* activity, int32_t flags) {
539    NativeCode* code = static_cast<NativeCode*>(activity);
540    write_work(code->mainWorkWrite, CMD_SHOW_SOFT_INPUT, flags);
541}
542
543void android_NativeActivity_hideSoftInput(
544        ANativeActivity* activity, int32_t flags) {
545    NativeCode* code = static_cast<NativeCode*>(activity);
546    write_work(code->mainWorkWrite, CMD_HIDE_SOFT_INPUT, flags);
547}
548
549// ------------------------------------------------------------------------
550
551static bool checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
552   if (env->ExceptionCheck()) {
553       LOGE("An exception was thrown by callback '%s'.", methodName);
554       LOGE_EX(env);
555       env->ExceptionClear();
556       return true;
557   }
558   return false;
559}
560
561/*
562 * Callback for handling native events on the application's main thread.
563 */
564static int mainWorkCallback(int fd, int events, void* data) {
565    NativeCode* code = (NativeCode*)data;
566    if ((events & POLLIN) == 0) {
567        return 1;
568    }
569
570    ActivityWork work;
571    if (!read_work(code->mainWorkRead, &work)) {
572        return 1;
573    }
574
575    LOG_TRACE("mainWorkCallback: cmd=%d", work.cmd);
576
577    switch (work.cmd) {
578        case CMD_DEF_KEY: {
579            KeyEvent* keyEvent;
580            while ((keyEvent=code->nativeInputQueue->consumeUnhandledEvent()) != NULL) {
581                jobject inputEventObj = android_view_KeyEvent_fromNative(
582                        code->env, keyEvent);
583                jboolean handled;
584                if (inputEventObj) {
585                    handled = code->env->CallBooleanMethod(code->clazz,
586                            gNativeActivityClassInfo.dispatchUnhandledKeyEvent, inputEventObj);
587                    checkAndClearExceptionFromCallback(code->env, "dispatchUnhandledKeyEvent");
588                    code->env->DeleteLocalRef(inputEventObj);
589                } else {
590                    LOGE("Failed to obtain key event for dispatchUnhandledKeyEvent.");
591                    handled = false;
592                }
593                code->nativeInputQueue->finishEvent(keyEvent, handled, true);
594            }
595            int seq;
596            while ((keyEvent=code->nativeInputQueue->consumePreDispatchingEvent(&seq)) != NULL) {
597                jobject inputEventObj = android_view_KeyEvent_fromNative(
598                        code->env, keyEvent);
599                if (inputEventObj) {
600                    code->env->CallVoidMethod(code->clazz,
601                            gNativeActivityClassInfo.preDispatchKeyEvent, inputEventObj, seq);
602                    checkAndClearExceptionFromCallback(code->env, "preDispatchKeyEvent");
603                    code->env->DeleteLocalRef(inputEventObj);
604                } else {
605                    LOGE("Failed to obtain key event for preDispatchKeyEvent.");
606                }
607            }
608        } break;
609        case CMD_FINISH: {
610            code->env->CallVoidMethod(code->clazz, gNativeActivityClassInfo.finish);
611            checkAndClearExceptionFromCallback(code->env, "finish");
612        } break;
613        case CMD_SET_WINDOW_FORMAT: {
614            code->env->CallVoidMethod(code->clazz,
615                    gNativeActivityClassInfo.setWindowFormat, work.arg1);
616            checkAndClearExceptionFromCallback(code->env, "setWindowFormat");
617        } break;
618        case CMD_SET_WINDOW_FLAGS: {
619            code->env->CallVoidMethod(code->clazz,
620                    gNativeActivityClassInfo.setWindowFlags, work.arg1, work.arg2);
621            checkAndClearExceptionFromCallback(code->env, "setWindowFlags");
622        } break;
623        case CMD_SHOW_SOFT_INPUT: {
624            code->env->CallVoidMethod(code->clazz,
625                    gNativeActivityClassInfo.showIme, work.arg1);
626            checkAndClearExceptionFromCallback(code->env, "showIme");
627        } break;
628        case CMD_HIDE_SOFT_INPUT: {
629            code->env->CallVoidMethod(code->clazz,
630                    gNativeActivityClassInfo.hideIme, work.arg1);
631            checkAndClearExceptionFromCallback(code->env, "hideIme");
632        } break;
633        default:
634            LOGW("Unknown work command: %d", work.cmd);
635            break;
636    }
637
638    return 1;
639}
640
641// ------------------------------------------------------------------------
642
643static jint
644loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path, jstring funcName,
645        jobject messageQueue, jstring internalDataDir, jstring obbDir,
646        jstring externalDataDir, int sdkVersion,
647        jobject jAssetMgr, jbyteArray savedState)
648{
649    LOG_TRACE("loadNativeCode_native");
650
651    const char* pathStr = env->GetStringUTFChars(path, NULL);
652    NativeCode* code = NULL;
653
654    void* handle = dlopen(pathStr, RTLD_LAZY);
655
656    env->ReleaseStringUTFChars(path, pathStr);
657
658    if (handle != NULL) {
659        const char* funcStr = env->GetStringUTFChars(funcName, NULL);
660        code = new NativeCode(handle, (ANativeActivity_createFunc*)
661                dlsym(handle, funcStr));
662        env->ReleaseStringUTFChars(funcName, funcStr);
663
664        if (code->createActivityFunc == NULL) {
665            LOGW("ANativeActivity_onCreate not found");
666            delete code;
667            return 0;
668        }
669
670        code->looper = android_os_MessageQueue_getLooper(env, messageQueue);
671        if (code->looper == NULL) {
672            LOGW("Unable to retrieve MessageQueue's Looper");
673            delete code;
674            return 0;
675        }
676
677        int msgpipe[2];
678        if (pipe(msgpipe)) {
679            LOGW("could not create pipe: %s", strerror(errno));
680            delete code;
681            return 0;
682        }
683        code->mainWorkRead = msgpipe[0];
684        code->mainWorkWrite = msgpipe[1];
685        int result = fcntl(code->mainWorkRead, F_SETFL, O_NONBLOCK);
686        SLOGW_IF(result != 0, "Could not make main work read pipe "
687                "non-blocking: %s", strerror(errno));
688        result = fcntl(code->mainWorkWrite, F_SETFL, O_NONBLOCK);
689        SLOGW_IF(result != 0, "Could not make main work write pipe "
690                "non-blocking: %s", strerror(errno));
691        code->looper->addFd(code->mainWorkRead, 0, ALOOPER_EVENT_INPUT, mainWorkCallback, code);
692
693        code->ANativeActivity::callbacks = &code->callbacks;
694        if (env->GetJavaVM(&code->vm) < 0) {
695            LOGW("NativeActivity GetJavaVM failed");
696            delete code;
697            return 0;
698        }
699        code->env = env;
700        code->clazz = env->NewGlobalRef(clazz);
701
702        const char* dirStr = env->GetStringUTFChars(internalDataDir, NULL);
703        code->internalDataPathObj = dirStr;
704        code->internalDataPath = code->internalDataPathObj.string();
705        env->ReleaseStringUTFChars(internalDataDir, dirStr);
706
707        dirStr = env->GetStringUTFChars(externalDataDir, NULL);
708        code->externalDataPathObj = dirStr;
709        code->externalDataPath = code->externalDataPathObj.string();
710        env->ReleaseStringUTFChars(externalDataDir, dirStr);
711
712        code->sdkVersion = sdkVersion;
713
714        code->assetManager = assetManagerForJavaObject(env, jAssetMgr);
715
716        dirStr = env->GetStringUTFChars(obbDir, NULL);
717        code->obbPathObj = dirStr;
718        code->obbPath = code->obbPathObj.string();
719        env->ReleaseStringUTFChars(obbDir, dirStr);
720
721        jbyte* rawSavedState = NULL;
722        jsize rawSavedSize = 0;
723        if (savedState != NULL) {
724            rawSavedState = env->GetByteArrayElements(savedState, NULL);
725            rawSavedSize = env->GetArrayLength(savedState);
726        }
727
728        code->createActivityFunc(code, rawSavedState, rawSavedSize);
729
730        if (rawSavedState != NULL) {
731            env->ReleaseByteArrayElements(savedState, rawSavedState, 0);
732        }
733    }
734
735    return (jint)code;
736}
737
738static void
739unloadNativeCode_native(JNIEnv* env, jobject clazz, jint handle)
740{
741    LOG_TRACE("unloadNativeCode_native");
742    if (handle != 0) {
743        NativeCode* code = (NativeCode*)handle;
744        delete code;
745    }
746}
747
748static void
749onStart_native(JNIEnv* env, jobject clazz, jint handle)
750{
751    LOG_TRACE("onStart_native");
752    if (handle != 0) {
753        NativeCode* code = (NativeCode*)handle;
754        if (code->callbacks.onStart != NULL) {
755            code->callbacks.onStart(code);
756        }
757    }
758}
759
760static void
761onResume_native(JNIEnv* env, jobject clazz, jint handle)
762{
763    LOG_TRACE("onResume_native");
764    if (handle != 0) {
765        NativeCode* code = (NativeCode*)handle;
766        if (code->callbacks.onResume != NULL) {
767            code->callbacks.onResume(code);
768        }
769    }
770}
771
772static jbyteArray
773onSaveInstanceState_native(JNIEnv* env, jobject clazz, jint handle)
774{
775    LOG_TRACE("onSaveInstanceState_native");
776
777    jbyteArray array = NULL;
778
779    if (handle != 0) {
780        NativeCode* code = (NativeCode*)handle;
781        if (code->callbacks.onSaveInstanceState != NULL) {
782            size_t len = 0;
783            jbyte* state = (jbyte*)code->callbacks.onSaveInstanceState(code, &len);
784            if (len > 0) {
785                array = env->NewByteArray(len);
786                if (array != NULL) {
787                    env->SetByteArrayRegion(array, 0, len, state);
788                }
789            }
790            if (state != NULL) {
791                free(state);
792            }
793        }
794    }
795
796    return array;
797}
798
799static void
800onPause_native(JNIEnv* env, jobject clazz, jint handle)
801{
802    LOG_TRACE("onPause_native");
803    if (handle != 0) {
804        NativeCode* code = (NativeCode*)handle;
805        if (code->callbacks.onPause != NULL) {
806            code->callbacks.onPause(code);
807        }
808    }
809}
810
811static void
812onStop_native(JNIEnv* env, jobject clazz, jint handle)
813{
814    LOG_TRACE("onStop_native");
815    if (handle != 0) {
816        NativeCode* code = (NativeCode*)handle;
817        if (code->callbacks.onStop != NULL) {
818            code->callbacks.onStop(code);
819        }
820    }
821}
822
823static void
824onConfigurationChanged_native(JNIEnv* env, jobject clazz, jint handle)
825{
826    LOG_TRACE("onConfigurationChanged_native");
827    if (handle != 0) {
828        NativeCode* code = (NativeCode*)handle;
829        if (code->callbacks.onConfigurationChanged != NULL) {
830            code->callbacks.onConfigurationChanged(code);
831        }
832    }
833}
834
835static void
836onLowMemory_native(JNIEnv* env, jobject clazz, jint handle)
837{
838    LOG_TRACE("onLowMemory_native");
839    if (handle != 0) {
840        NativeCode* code = (NativeCode*)handle;
841        if (code->callbacks.onLowMemory != NULL) {
842            code->callbacks.onLowMemory(code);
843        }
844    }
845}
846
847static void
848onWindowFocusChanged_native(JNIEnv* env, jobject clazz, jint handle, jboolean focused)
849{
850    LOG_TRACE("onWindowFocusChanged_native");
851    if (handle != 0) {
852        NativeCode* code = (NativeCode*)handle;
853        if (code->callbacks.onWindowFocusChanged != NULL) {
854            code->callbacks.onWindowFocusChanged(code, focused ? 1 : 0);
855        }
856    }
857}
858
859static void
860onSurfaceCreated_native(JNIEnv* env, jobject clazz, jint handle, jobject surface)
861{
862    LOG_TRACE("onSurfaceCreated_native");
863    if (handle != 0) {
864        NativeCode* code = (NativeCode*)handle;
865        code->setSurface(surface);
866        if (code->nativeWindow != NULL && code->callbacks.onNativeWindowCreated != NULL) {
867            code->callbacks.onNativeWindowCreated(code,
868                    code->nativeWindow.get());
869        }
870    }
871}
872
873static int32_t getWindowProp(ANativeWindow* window, int what) {
874    int value;
875    int res = window->query(window, what, &value);
876    return res < 0 ? res : value;
877}
878
879static void
880onSurfaceChanged_native(JNIEnv* env, jobject clazz, jint handle, jobject surface,
881        jint format, jint width, jint height)
882{
883    LOG_TRACE("onSurfaceChanged_native");
884    if (handle != 0) {
885        NativeCode* code = (NativeCode*)handle;
886        sp<ANativeWindow> oldNativeWindow = code->nativeWindow;
887        code->setSurface(surface);
888        if (oldNativeWindow != code->nativeWindow) {
889            if (oldNativeWindow != NULL && code->callbacks.onNativeWindowDestroyed != NULL) {
890                code->callbacks.onNativeWindowDestroyed(code,
891                        oldNativeWindow.get());
892            }
893            if (code->nativeWindow != NULL) {
894                if (code->callbacks.onNativeWindowCreated != NULL) {
895                    code->callbacks.onNativeWindowCreated(code,
896                            code->nativeWindow.get());
897                }
898                code->lastWindowWidth = getWindowProp(code->nativeWindow.get(),
899                        NATIVE_WINDOW_WIDTH);
900                code->lastWindowHeight = getWindowProp(code->nativeWindow.get(),
901                        NATIVE_WINDOW_HEIGHT);
902            }
903        } else {
904            // Maybe it resized?
905            int32_t newWidth = getWindowProp(code->nativeWindow.get(),
906                    NATIVE_WINDOW_WIDTH);
907            int32_t newHeight = getWindowProp(code->nativeWindow.get(),
908                    NATIVE_WINDOW_HEIGHT);
909            if (newWidth != code->lastWindowWidth
910                    || newHeight != code->lastWindowHeight) {
911                if (code->callbacks.onNativeWindowResized != NULL) {
912                    code->callbacks.onNativeWindowResized(code,
913                            code->nativeWindow.get());
914                }
915            }
916        }
917    }
918}
919
920static void
921onSurfaceRedrawNeeded_native(JNIEnv* env, jobject clazz, jint handle)
922{
923    LOG_TRACE("onSurfaceRedrawNeeded_native");
924    if (handle != 0) {
925        NativeCode* code = (NativeCode*)handle;
926        if (code->nativeWindow != NULL && code->callbacks.onNativeWindowRedrawNeeded != NULL) {
927            code->callbacks.onNativeWindowRedrawNeeded(code, code->nativeWindow.get());
928        }
929    }
930}
931
932static void
933onSurfaceDestroyed_native(JNIEnv* env, jobject clazz, jint handle, jobject surface)
934{
935    LOG_TRACE("onSurfaceDestroyed_native");
936    if (handle != 0) {
937        NativeCode* code = (NativeCode*)handle;
938        if (code->nativeWindow != NULL && code->callbacks.onNativeWindowDestroyed != NULL) {
939            code->callbacks.onNativeWindowDestroyed(code,
940                    code->nativeWindow.get());
941        }
942        code->setSurface(NULL);
943    }
944}
945
946static void
947onInputChannelCreated_native(JNIEnv* env, jobject clazz, jint handle, jobject channel)
948{
949    LOG_TRACE("onInputChannelCreated_native");
950    if (handle != 0) {
951        NativeCode* code = (NativeCode*)handle;
952        status_t err = code->setInputChannel(channel);
953        if (err != OK) {
954            jniThrowException(env, "java/lang/IllegalStateException",
955                    "Error setting input channel");
956            return;
957        }
958        if (code->callbacks.onInputQueueCreated != NULL) {
959            code->callbacks.onInputQueueCreated(code,
960                    code->nativeInputQueue);
961        }
962    }
963}
964
965static void
966onInputChannelDestroyed_native(JNIEnv* env, jobject clazz, jint handle, jobject channel)
967{
968    LOG_TRACE("onInputChannelDestroyed_native");
969    if (handle != 0) {
970        NativeCode* code = (NativeCode*)handle;
971        if (code->nativeInputQueue != NULL
972                && code->callbacks.onInputQueueDestroyed != NULL) {
973            code->callbacks.onInputQueueDestroyed(code,
974                    code->nativeInputQueue);
975        }
976        code->setInputChannel(NULL);
977    }
978}
979
980static void
981onContentRectChanged_native(JNIEnv* env, jobject clazz, jint handle,
982        jint x, jint y, jint w, jint h)
983{
984    LOG_TRACE("onContentRectChanged_native");
985    if (handle != 0) {
986        NativeCode* code = (NativeCode*)handle;
987        if (code->callbacks.onContentRectChanged != NULL) {
988            ARect rect;
989            rect.left = x;
990            rect.top = y;
991            rect.right = x+w;
992            rect.bottom = y+h;
993            code->callbacks.onContentRectChanged(code, &rect);
994        }
995    }
996}
997
998static void
999dispatchKeyEvent_native(JNIEnv* env, jobject clazz, jint handle, jobject eventObj)
1000{
1001    LOG_TRACE("dispatchKeyEvent_native");
1002    if (handle != 0) {
1003        NativeCode* code = (NativeCode*)handle;
1004        if (code->nativeInputQueue != NULL) {
1005            KeyEvent* event = code->nativeInputQueue->createKeyEvent();
1006            status_t status = android_view_KeyEvent_toNative(env, eventObj, event);
1007            if (status) {
1008                delete event;
1009                jniThrowRuntimeException(env, "Could not read contents of KeyEvent object.");
1010                return;
1011            }
1012            code->nativeInputQueue->dispatchEvent(event);
1013        }
1014    }
1015}
1016
1017static void
1018finishPreDispatchKeyEvent_native(JNIEnv* env, jobject clazz, jint handle,
1019        jint seq, jboolean handled)
1020{
1021    LOG_TRACE("finishPreDispatchKeyEvent_native");
1022    if (handle != 0) {
1023        NativeCode* code = (NativeCode*)handle;
1024        if (code->nativeInputQueue != NULL) {
1025            code->nativeInputQueue->finishPreDispatch(seq, handled ? true : false);
1026        }
1027    }
1028}
1029
1030static const JNINativeMethod g_methods[] = {
1031    { "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",
1032            (void*)loadNativeCode_native },
1033    { "unloadNativeCode", "(I)V", (void*)unloadNativeCode_native },
1034    { "onStartNative", "(I)V", (void*)onStart_native },
1035    { "onResumeNative", "(I)V", (void*)onResume_native },
1036    { "onSaveInstanceStateNative", "(I)[B", (void*)onSaveInstanceState_native },
1037    { "onPauseNative", "(I)V", (void*)onPause_native },
1038    { "onStopNative", "(I)V", (void*)onStop_native },
1039    { "onConfigurationChangedNative", "(I)V", (void*)onConfigurationChanged_native },
1040    { "onLowMemoryNative", "(I)V", (void*)onLowMemory_native },
1041    { "onWindowFocusChangedNative", "(IZ)V", (void*)onWindowFocusChanged_native },
1042    { "onSurfaceCreatedNative", "(ILandroid/view/Surface;)V", (void*)onSurfaceCreated_native },
1043    { "onSurfaceChangedNative", "(ILandroid/view/Surface;III)V", (void*)onSurfaceChanged_native },
1044    { "onSurfaceRedrawNeededNative", "(ILandroid/view/Surface;)V", (void*)onSurfaceRedrawNeeded_native },
1045    { "onSurfaceDestroyedNative", "(I)V", (void*)onSurfaceDestroyed_native },
1046    { "onInputChannelCreatedNative", "(ILandroid/view/InputChannel;)V", (void*)onInputChannelCreated_native },
1047    { "onInputChannelDestroyedNative", "(ILandroid/view/InputChannel;)V", (void*)onInputChannelDestroyed_native },
1048    { "onContentRectChangedNative", "(IIIII)V", (void*)onContentRectChanged_native },
1049    { "dispatchKeyEventNative", "(ILandroid/view/KeyEvent;)V", (void*)dispatchKeyEvent_native },
1050    { "finishPreDispatchKeyEventNative", "(IIZ)V", (void*)finishPreDispatchKeyEvent_native },
1051};
1052
1053static const char* const kNativeActivityPathName = "android/app/NativeActivity";
1054
1055#define FIND_CLASS(var, className) \
1056        var = env->FindClass(className); \
1057        LOG_FATAL_IF(! var, "Unable to find class %s", className); \
1058        var = jclass(env->NewGlobalRef(var));
1059
1060#define GET_METHOD_ID(var, clazz, methodName, fieldDescriptor) \
1061        var = env->GetMethodID(clazz, methodName, fieldDescriptor); \
1062        LOG_FATAL_IF(! var, "Unable to find method" methodName);
1063
1064int register_android_app_NativeActivity(JNIEnv* env)
1065{
1066    //LOGD("register_android_app_NativeActivity");
1067
1068    FIND_CLASS(gNativeActivityClassInfo.clazz, kNativeActivityPathName);
1069
1070    GET_METHOD_ID(gNativeActivityClassInfo.dispatchUnhandledKeyEvent,
1071            gNativeActivityClassInfo.clazz,
1072            "dispatchUnhandledKeyEvent", "(Landroid/view/KeyEvent;)Z");
1073    GET_METHOD_ID(gNativeActivityClassInfo.preDispatchKeyEvent,
1074            gNativeActivityClassInfo.clazz,
1075            "preDispatchKeyEvent", "(Landroid/view/KeyEvent;I)V");
1076
1077    GET_METHOD_ID(gNativeActivityClassInfo.finish,
1078            gNativeActivityClassInfo.clazz,
1079            "finish", "()V");
1080    GET_METHOD_ID(gNativeActivityClassInfo.setWindowFlags,
1081            gNativeActivityClassInfo.clazz,
1082            "setWindowFlags", "(II)V");
1083    GET_METHOD_ID(gNativeActivityClassInfo.setWindowFormat,
1084            gNativeActivityClassInfo.clazz,
1085            "setWindowFormat", "(I)V");
1086    GET_METHOD_ID(gNativeActivityClassInfo.showIme,
1087            gNativeActivityClassInfo.clazz,
1088            "showIme", "(I)V");
1089    GET_METHOD_ID(gNativeActivityClassInfo.hideIme,
1090            gNativeActivityClassInfo.clazz,
1091            "hideIme", "(I)V");
1092
1093    return AndroidRuntime::registerNativeMethods(
1094        env, kNativeActivityPathName,
1095        g_methods, NELEM(g_methods));
1096}
1097
1098} // namespace android
1099