android_app_NativeActivity.cpp revision e21d91c694e828e6285cc50e79b2a6be6e8c692b
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);
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();
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) {
249    LOG_TRACE("finishEvent: %p handled=%d", event, handled ? 1 : 0);
250
251    if (!handled && ((InputEvent*)event)->getType() == AINPUT_EVENT_TYPE_KEY
252            && ((KeyEvent*)event)->hasDefaultAction()) {
253        // The app didn't handle this, but it may have a default action
254        // associated with it.  We need to hand this back to Java to be
255        // executed.
256        doUnhandledKey((KeyEvent*)event);
257        return;
258    }
259
260    mLock.lock();
261    const size_t N = mInFlightEvents.size();
262    for (size_t i=0; i<N; i++) {
263        const in_flight_event& inflight(mInFlightEvents[i]);
264        if (inflight.event == event) {
265            if (inflight.doFinish) {
266                int32_t res = mConsumer.sendFinishedSignal();
267                if (res != android::OK) {
268                    LOGW("Failed to send finished signal on channel '%s'.  status=%d",
269                            mConsumer.getChannel()->getName().string(), res);
270                }
271            }
272            if (static_cast<InputEvent*>(event)->getType() == AINPUT_EVENT_TYPE_KEY) {
273                mAvailKeyEvents.push(static_cast<KeyEvent*>(event));
274            } else {
275                mAvailMotionEvents.push(static_cast<MotionEvent*>(event));
276            }
277            mInFlightEvents.removeAt(i);
278            mLock.unlock();
279            return;
280        }
281    }
282    mLock.unlock();
283
284    LOGW("finishEvent called for unknown event: %p", event);
285}
286
287void AInputQueue::dispatchEvent(android::KeyEvent* event) {
288    mLock.lock();
289    LOG_TRACE("dispatchEvent: dispatching=%d write=%d\n", mDispatchingKeys.size(),
290            mDispatchKeyWrite);
291    mDispatchingKeys.add(event);
292    wakeupDispatch();
293    mLock.unlock();
294}
295
296void AInputQueue::finishPreDispatch(int seq, bool handled) {
297    mLock.lock();
298    LOG_TRACE("finishPreDispatch: seq=%d handled=%d\n", seq, handled ? 1 : 0);
299    finish_pre_dispatch finish;
300    finish.seq = seq;
301    finish.handled = handled;
302    mFinishPreDispatches.add(finish);
303    wakeupDispatch();
304    mLock.unlock();
305}
306
307KeyEvent* AInputQueue::consumeUnhandledEvent() {
308    KeyEvent* event = NULL;
309
310    mLock.lock();
311    if (mUnhandledKeys.size() > 0) {
312        event = mUnhandledKeys[0];
313        mUnhandledKeys.removeAt(0);
314    }
315    mLock.unlock();
316
317    LOG_TRACE("consumeUnhandledEvent: KeyEvent=%p", event);
318
319    return event;
320}
321
322KeyEvent* AInputQueue::consumePreDispatchingEvent(int* outSeq) {
323    KeyEvent* event = NULL;
324
325    mLock.lock();
326    if (mPreDispatchingKeys.size() > 0) {
327        const in_flight_event& inflight(mPreDispatchingKeys[0]);
328        event = static_cast<KeyEvent*>(inflight.event);
329        *outSeq = inflight.seq;
330        mPreDispatchingKeys.removeAt(0);
331    }
332    mLock.unlock();
333
334    LOG_TRACE("consumePreDispatchingEvent: KeyEvent=%p", event);
335
336    return event;
337}
338
339KeyEvent* AInputQueue::createKeyEvent() {
340    mLock.lock();
341    KeyEvent* event;
342    if (mAvailKeyEvents.size() <= 0) {
343        event = new KeyEvent();
344    } else {
345        event = mAvailKeyEvents.top();
346        mAvailKeyEvents.pop();
347    }
348    mLock.unlock();
349    return event;
350}
351
352MotionEvent* AInputQueue::createMotionEvent() {
353    mLock.lock();
354    MotionEvent* event;
355    if (mAvailMotionEvents.size() <= 0) {
356        event = new MotionEvent();
357    } else {
358        event = mAvailMotionEvents.top();
359        mAvailMotionEvents.pop();
360    }
361    mLock.unlock();
362    return event;
363}
364
365void AInputQueue::doUnhandledKey(KeyEvent* keyEvent) {
366    mLock.lock();
367    LOG_TRACE("Unhandled key: pending=%d write=%d\n", mUnhandledKeys.size(), mWorkWrite);
368    if (mUnhandledKeys.size() <= 0 && mWorkWrite >= 0) {
369        write_work(mWorkWrite, CMD_DEF_KEY);
370    }
371    mUnhandledKeys.add(keyEvent);
372    mLock.unlock();
373}
374
375bool AInputQueue::preDispatchKey(KeyEvent* keyEvent) {
376    mLock.lock();
377    LOG_TRACE("preDispatch key: pending=%d write=%d\n", mPreDispatchingKeys.size(), mWorkWrite);
378    const size_t N = mInFlightEvents.size();
379    for (size_t i=0; i<N; i++) {
380        in_flight_event& inflight(mInFlightEvents.editItemAt(i));
381        if (inflight.event == keyEvent) {
382            if (inflight.seq >= 0) {
383                // This event has already been pre-dispatched!
384                LOG_TRACE("Event already pre-dispatched!");
385                mLock.unlock();
386                return false;
387            }
388            mSeq++;
389            if (mSeq < 0) mSeq = 1;
390            inflight.seq = mSeq;
391
392            if (mPreDispatchingKeys.size() <= 0 && mWorkWrite >= 0) {
393                write_work(mWorkWrite, CMD_DEF_KEY);
394            }
395            mPreDispatchingKeys.add(inflight);
396            mLock.unlock();
397            return true;
398        }
399    }
400
401    LOGW("preDispatchKey called for unknown event: %p", keyEvent);
402    return false;
403}
404
405void AInputQueue::wakeupDispatch() {
406restart:
407    char dummy = 0;
408    int res = write(mDispatchKeyWrite, &dummy, sizeof(dummy));
409    if (res < 0 && errno == EINTR) {
410        goto restart;
411    }
412
413    if (res == sizeof(dummy)) return;
414
415    if (res < 0) LOGW("Failed writing to dispatch fd: %s", strerror(errno));
416    else LOGW("Truncated writing to dispatch fd: %d", res);
417}
418
419namespace android {
420
421// ------------------------------------------------------------------------
422
423/*
424 * Native state for interacting with the NativeActivity class.
425 */
426struct NativeCode : public ANativeActivity {
427    NativeCode(void* _dlhandle, ANativeActivity_createFunc* _createFunc) {
428        memset((ANativeActivity*)this, 0, sizeof(ANativeActivity));
429        memset(&callbacks, 0, sizeof(callbacks));
430        dlhandle = _dlhandle;
431        createActivityFunc = _createFunc;
432        nativeWindow = NULL;
433        inputChannel = NULL;
434        nativeInputQueue = NULL;
435        mainWorkRead = mainWorkWrite = -1;
436    }
437
438    ~NativeCode() {
439        if (callbacks.onDestroy != NULL) {
440            callbacks.onDestroy(this);
441        }
442        if (env != NULL && clazz != NULL) {
443            env->DeleteGlobalRef(clazz);
444        }
445        if (looper != NULL && mainWorkRead >= 0) {
446            looper->removeFd(mainWorkRead);
447        }
448        if (nativeInputQueue != NULL) {
449            nativeInputQueue->mWorkWrite = -1;
450        }
451        setSurface(NULL);
452        setInputChannel(NULL);
453        if (mainWorkRead >= 0) close(mainWorkRead);
454        if (mainWorkWrite >= 0) close(mainWorkWrite);
455        if (dlhandle != NULL) {
456            // for now don't unload...  we probably should clean this
457            // up and only keep one open dlhandle per proc, since there
458            // is really no benefit to unloading the code.
459            //dlclose(dlhandle);
460        }
461    }
462
463    void setSurface(jobject _surface) {
464        if (_surface != NULL) {
465            nativeWindow = android_Surface_getNativeWindow(env, _surface);
466        } else {
467            nativeWindow = NULL;
468        }
469    }
470
471    status_t setInputChannel(jobject _channel) {
472        if (inputChannel != NULL) {
473            delete nativeInputQueue;
474            env->DeleteGlobalRef(inputChannel);
475        }
476        inputChannel = NULL;
477        nativeInputQueue = NULL;
478        if (_channel != NULL) {
479            inputChannel = env->NewGlobalRef(_channel);
480            sp<InputChannel> ic =
481                    android_view_InputChannel_getInputChannel(env, _channel);
482            if (ic != NULL) {
483                nativeInputQueue = new AInputQueue(ic, mainWorkWrite);
484                if (nativeInputQueue->getConsumer().initialize() != android::OK) {
485                    delete nativeInputQueue;
486                    nativeInputQueue = NULL;
487                    return UNKNOWN_ERROR;
488                }
489            } else {
490                return UNKNOWN_ERROR;
491            }
492        }
493        return OK;
494    }
495
496    ANativeActivityCallbacks callbacks;
497
498    void* dlhandle;
499    ANativeActivity_createFunc* createActivityFunc;
500
501    String8 internalDataPath;
502    String8 externalDataPath;
503
504    sp<ANativeWindow> nativeWindow;
505    int32_t lastWindowWidth;
506    int32_t lastWindowHeight;
507
508    jobject inputChannel;
509    struct AInputQueue* nativeInputQueue;
510
511    // These are used to wake up the main thread to process work.
512    int mainWorkRead;
513    int mainWorkWrite;
514    sp<Looper> looper;
515};
516
517void android_NativeActivity_finish(ANativeActivity* activity) {
518    NativeCode* code = static_cast<NativeCode*>(activity);
519    write_work(code->mainWorkWrite, CMD_FINISH, 0);
520}
521
522void android_NativeActivity_setWindowFormat(
523        ANativeActivity* activity, int32_t format) {
524    NativeCode* code = static_cast<NativeCode*>(activity);
525    write_work(code->mainWorkWrite, CMD_SET_WINDOW_FORMAT, format);
526}
527
528void android_NativeActivity_setWindowFlags(
529        ANativeActivity* activity, int32_t values, int32_t mask) {
530    NativeCode* code = static_cast<NativeCode*>(activity);
531    write_work(code->mainWorkWrite, CMD_SET_WINDOW_FLAGS, values, mask);
532}
533
534void android_NativeActivity_showSoftInput(
535        ANativeActivity* activity, int32_t flags) {
536    NativeCode* code = static_cast<NativeCode*>(activity);
537    write_work(code->mainWorkWrite, CMD_SHOW_SOFT_INPUT, flags);
538}
539
540void android_NativeActivity_hideSoftInput(
541        ANativeActivity* activity, int32_t flags) {
542    NativeCode* code = static_cast<NativeCode*>(activity);
543    write_work(code->mainWorkWrite, CMD_HIDE_SOFT_INPUT, flags);
544}
545
546// ------------------------------------------------------------------------
547
548static bool checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
549   if (env->ExceptionCheck()) {
550       LOGE("An exception was thrown by callback '%s'.", methodName);
551       LOGE_EX(env);
552       env->ExceptionClear();
553       return true;
554   }
555   return false;
556}
557
558/*
559 * Callback for handling native events on the application's main thread.
560 */
561static int mainWorkCallback(int fd, int events, void* data) {
562    NativeCode* code = (NativeCode*)data;
563    if ((events & POLLIN) == 0) {
564        return 1;
565    }
566
567    ActivityWork work;
568    if (!read_work(code->mainWorkRead, &work)) {
569        return 1;
570    }
571
572    LOG_TRACE("mainWorkCallback: cmd=%d", work.cmd);
573
574    switch (work.cmd) {
575        case CMD_DEF_KEY: {
576            KeyEvent* keyEvent;
577            while ((keyEvent=code->nativeInputQueue->consumeUnhandledEvent()) != NULL) {
578                jobject inputEventObj = android_view_KeyEvent_fromNative(
579                        code->env, keyEvent);
580                code->env->CallVoidMethod(code->clazz,
581                        gNativeActivityClassInfo.dispatchUnhandledKeyEvent, inputEventObj);
582                checkAndClearExceptionFromCallback(code->env, "dispatchUnhandledKeyEvent");
583                code->nativeInputQueue->finishEvent(keyEvent, true);
584            }
585            int seq;
586            while ((keyEvent=code->nativeInputQueue->consumePreDispatchingEvent(&seq)) != NULL) {
587                jobject inputEventObj = android_view_KeyEvent_fromNative(
588                        code->env, keyEvent);
589                code->env->CallVoidMethod(code->clazz,
590                        gNativeActivityClassInfo.preDispatchKeyEvent, inputEventObj, seq);
591                checkAndClearExceptionFromCallback(code->env, "preDispatchKeyEvent");
592            }
593        } break;
594        case CMD_FINISH: {
595            code->env->CallVoidMethod(code->clazz, gNativeActivityClassInfo.finish);
596            checkAndClearExceptionFromCallback(code->env, "finish");
597        } break;
598        case CMD_SET_WINDOW_FORMAT: {
599            code->env->CallVoidMethod(code->clazz,
600                    gNativeActivityClassInfo.setWindowFormat, work.arg1);
601            checkAndClearExceptionFromCallback(code->env, "setWindowFormat");
602        } break;
603        case CMD_SET_WINDOW_FLAGS: {
604            code->env->CallVoidMethod(code->clazz,
605                    gNativeActivityClassInfo.setWindowFlags, work.arg1, work.arg2);
606            checkAndClearExceptionFromCallback(code->env, "setWindowFlags");
607        } break;
608        case CMD_SHOW_SOFT_INPUT: {
609            code->env->CallVoidMethod(code->clazz,
610                    gNativeActivityClassInfo.showIme, work.arg1);
611            checkAndClearExceptionFromCallback(code->env, "showIme");
612        } break;
613        case CMD_HIDE_SOFT_INPUT: {
614            code->env->CallVoidMethod(code->clazz,
615                    gNativeActivityClassInfo.hideIme, work.arg1);
616            checkAndClearExceptionFromCallback(code->env, "hideIme");
617        } break;
618        default:
619            LOGW("Unknown work command: %d", work.cmd);
620            break;
621    }
622
623    return 1;
624}
625
626// ------------------------------------------------------------------------
627
628static jint
629loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path, jstring funcName,
630        jobject messageQueue,
631        jstring internalDataDir, jstring externalDataDir, int sdkVersion,
632        jobject jAssetMgr, jbyteArray savedState)
633{
634    LOG_TRACE("loadNativeCode_native");
635
636    const char* pathStr = env->GetStringUTFChars(path, NULL);
637    NativeCode* code = NULL;
638
639    void* handle = dlopen(pathStr, RTLD_LAZY);
640
641    env->ReleaseStringUTFChars(path, pathStr);
642
643    if (handle != NULL) {
644        const char* funcStr = env->GetStringUTFChars(funcName, NULL);
645        code = new NativeCode(handle, (ANativeActivity_createFunc*)
646                dlsym(handle, funcStr));
647        env->ReleaseStringUTFChars(funcName, funcStr);
648
649        if (code->createActivityFunc == NULL) {
650            LOGW("ANativeActivity_onCreate not found");
651            delete code;
652            return 0;
653        }
654
655        code->looper = android_os_MessageQueue_getLooper(env, messageQueue);
656        if (code->looper == NULL) {
657            LOGW("Unable to retrieve MessageQueue's Looper");
658            delete code;
659            return 0;
660        }
661
662        int msgpipe[2];
663        if (pipe(msgpipe)) {
664            LOGW("could not create pipe: %s", strerror(errno));
665            delete code;
666            return 0;
667        }
668        code->mainWorkRead = msgpipe[0];
669        code->mainWorkWrite = msgpipe[1];
670        int result = fcntl(code->mainWorkRead, F_SETFL, O_NONBLOCK);
671        SLOGW_IF(result != 0, "Could not make main work read pipe "
672                "non-blocking: %s", strerror(errno));
673        result = fcntl(code->mainWorkWrite, F_SETFL, O_NONBLOCK);
674        SLOGW_IF(result != 0, "Could not make main work write pipe "
675                "non-blocking: %s", strerror(errno));
676        code->looper->addFd(code->mainWorkRead, 0, ALOOPER_EVENT_INPUT, mainWorkCallback, code);
677
678        code->ANativeActivity::callbacks = &code->callbacks;
679        if (env->GetJavaVM(&code->vm) < 0) {
680            LOGW("NativeActivity GetJavaVM failed");
681            delete code;
682            return 0;
683        }
684        code->env = env;
685        code->clazz = env->NewGlobalRef(clazz);
686
687        const char* dirStr = env->GetStringUTFChars(internalDataDir, NULL);
688        code->internalDataPath = dirStr;
689        code->internalDataPath = code->internalDataPath.string();
690        env->ReleaseStringUTFChars(path, dirStr);
691
692        dirStr = env->GetStringUTFChars(externalDataDir, NULL);
693        code->externalDataPath = dirStr;
694        code->externalDataPath = code->externalDataPath.string();
695        env->ReleaseStringUTFChars(path, dirStr);
696
697        code->sdkVersion = sdkVersion;
698
699        code->assetManager = assetManagerForJavaObject(env, jAssetMgr);
700
701        jbyte* rawSavedState = NULL;
702        jsize rawSavedSize = 0;
703        if (savedState != NULL) {
704            rawSavedState = env->GetByteArrayElements(savedState, NULL);
705            rawSavedSize = env->GetArrayLength(savedState);
706        }
707
708        code->createActivityFunc(code, rawSavedState, rawSavedSize);
709
710        if (rawSavedState != NULL) {
711            env->ReleaseByteArrayElements(savedState, rawSavedState, 0);
712        }
713    }
714
715    return (jint)code;
716}
717
718static void
719unloadNativeCode_native(JNIEnv* env, jobject clazz, jint handle)
720{
721    LOG_TRACE("unloadNativeCode_native");
722    if (handle != 0) {
723        NativeCode* code = (NativeCode*)handle;
724        delete code;
725    }
726}
727
728static void
729onStart_native(JNIEnv* env, jobject clazz, jint handle)
730{
731    LOG_TRACE("onStart_native");
732    if (handle != 0) {
733        NativeCode* code = (NativeCode*)handle;
734        if (code->callbacks.onStart != NULL) {
735            code->callbacks.onStart(code);
736        }
737    }
738}
739
740static void
741onResume_native(JNIEnv* env, jobject clazz, jint handle)
742{
743    LOG_TRACE("onResume_native");
744    if (handle != 0) {
745        NativeCode* code = (NativeCode*)handle;
746        if (code->callbacks.onResume != NULL) {
747            code->callbacks.onResume(code);
748        }
749    }
750}
751
752static jbyteArray
753onSaveInstanceState_native(JNIEnv* env, jobject clazz, jint handle)
754{
755    LOG_TRACE("onSaveInstanceState_native");
756
757    jbyteArray array = NULL;
758
759    if (handle != 0) {
760        NativeCode* code = (NativeCode*)handle;
761        if (code->callbacks.onSaveInstanceState != NULL) {
762            size_t len = 0;
763            jbyte* state = (jbyte*)code->callbacks.onSaveInstanceState(code, &len);
764            if (len > 0) {
765                array = env->NewByteArray(len);
766                if (array != NULL) {
767                    env->SetByteArrayRegion(array, 0, len, state);
768                }
769            }
770            if (state != NULL) {
771                free(state);
772            }
773        }
774    }
775
776    return array;
777}
778
779static void
780onPause_native(JNIEnv* env, jobject clazz, jint handle)
781{
782    LOG_TRACE("onPause_native");
783    if (handle != 0) {
784        NativeCode* code = (NativeCode*)handle;
785        if (code->callbacks.onPause != NULL) {
786            code->callbacks.onPause(code);
787        }
788    }
789}
790
791static void
792onStop_native(JNIEnv* env, jobject clazz, jint handle)
793{
794    LOG_TRACE("onStop_native");
795    if (handle != 0) {
796        NativeCode* code = (NativeCode*)handle;
797        if (code->callbacks.onStop != NULL) {
798            code->callbacks.onStop(code);
799        }
800    }
801}
802
803static void
804onConfigurationChanged_native(JNIEnv* env, jobject clazz, jint handle)
805{
806    LOG_TRACE("onConfigurationChanged_native");
807    if (handle != 0) {
808        NativeCode* code = (NativeCode*)handle;
809        if (code->callbacks.onConfigurationChanged != NULL) {
810            code->callbacks.onConfigurationChanged(code);
811        }
812    }
813}
814
815static void
816onLowMemory_native(JNIEnv* env, jobject clazz, jint handle)
817{
818    LOG_TRACE("onLowMemory_native");
819    if (handle != 0) {
820        NativeCode* code = (NativeCode*)handle;
821        if (code->callbacks.onLowMemory != NULL) {
822            code->callbacks.onLowMemory(code);
823        }
824    }
825}
826
827static void
828onWindowFocusChanged_native(JNIEnv* env, jobject clazz, jint handle, jboolean focused)
829{
830    LOG_TRACE("onWindowFocusChanged_native");
831    if (handle != 0) {
832        NativeCode* code = (NativeCode*)handle;
833        if (code->callbacks.onWindowFocusChanged != NULL) {
834            code->callbacks.onWindowFocusChanged(code, focused ? 1 : 0);
835        }
836    }
837}
838
839static void
840onSurfaceCreated_native(JNIEnv* env, jobject clazz, jint handle, jobject surface)
841{
842    LOG_TRACE("onSurfaceCreated_native");
843    if (handle != 0) {
844        NativeCode* code = (NativeCode*)handle;
845        code->setSurface(surface);
846        if (code->nativeWindow != NULL && code->callbacks.onNativeWindowCreated != NULL) {
847            code->callbacks.onNativeWindowCreated(code,
848                    code->nativeWindow.get());
849        }
850    }
851}
852
853static int32_t getWindowProp(ANativeWindow* window, int what) {
854    int value;
855    int res = window->query(window, what, &value);
856    return res < 0 ? res : value;
857}
858
859static void
860onSurfaceChanged_native(JNIEnv* env, jobject clazz, jint handle, jobject surface,
861        jint format, jint width, jint height)
862{
863    LOG_TRACE("onSurfaceChanged_native");
864    if (handle != 0) {
865        NativeCode* code = (NativeCode*)handle;
866        sp<ANativeWindow> oldNativeWindow = code->nativeWindow;
867        code->setSurface(surface);
868        if (oldNativeWindow != code->nativeWindow) {
869            if (oldNativeWindow != NULL && code->callbacks.onNativeWindowDestroyed != NULL) {
870                code->callbacks.onNativeWindowDestroyed(code,
871                        oldNativeWindow.get());
872            }
873            if (code->nativeWindow != NULL) {
874                if (code->callbacks.onNativeWindowCreated != NULL) {
875                    code->callbacks.onNativeWindowCreated(code,
876                            code->nativeWindow.get());
877                }
878                code->lastWindowWidth = getWindowProp(code->nativeWindow.get(),
879                        NATIVE_WINDOW_WIDTH);
880                code->lastWindowHeight = getWindowProp(code->nativeWindow.get(),
881                        NATIVE_WINDOW_HEIGHT);
882            }
883        } else {
884            // Maybe it resized?
885            int32_t newWidth = getWindowProp(code->nativeWindow.get(),
886                    NATIVE_WINDOW_WIDTH);
887            int32_t newHeight = getWindowProp(code->nativeWindow.get(),
888                    NATIVE_WINDOW_HEIGHT);
889            if (newWidth != code->lastWindowWidth
890                    || newHeight != code->lastWindowHeight) {
891                if (code->callbacks.onNativeWindowResized != NULL) {
892                    code->callbacks.onNativeWindowResized(code,
893                            code->nativeWindow.get());
894                }
895            }
896        }
897    }
898}
899
900static void
901onSurfaceRedrawNeeded_native(JNIEnv* env, jobject clazz, jint handle)
902{
903    LOG_TRACE("onSurfaceRedrawNeeded_native");
904    if (handle != 0) {
905        NativeCode* code = (NativeCode*)handle;
906        if (code->nativeWindow != NULL && code->callbacks.onNativeWindowRedrawNeeded != NULL) {
907            code->callbacks.onNativeWindowRedrawNeeded(code, code->nativeWindow.get());
908        }
909    }
910}
911
912static void
913onSurfaceDestroyed_native(JNIEnv* env, jobject clazz, jint handle, jobject surface)
914{
915    LOG_TRACE("onSurfaceDestroyed_native");
916    if (handle != 0) {
917        NativeCode* code = (NativeCode*)handle;
918        if (code->nativeWindow != NULL && code->callbacks.onNativeWindowDestroyed != NULL) {
919            code->callbacks.onNativeWindowDestroyed(code,
920                    code->nativeWindow.get());
921        }
922        code->setSurface(NULL);
923    }
924}
925
926static void
927onInputChannelCreated_native(JNIEnv* env, jobject clazz, jint handle, jobject channel)
928{
929    LOG_TRACE("onInputChannelCreated_native");
930    if (handle != 0) {
931        NativeCode* code = (NativeCode*)handle;
932        status_t err = code->setInputChannel(channel);
933        if (err != OK) {
934            jniThrowException(env, "java/lang/IllegalStateException",
935                    "Error setting input channel");
936            return;
937        }
938        if (code->callbacks.onInputQueueCreated != NULL) {
939            code->callbacks.onInputQueueCreated(code,
940                    code->nativeInputQueue);
941        }
942    }
943}
944
945static void
946onInputChannelDestroyed_native(JNIEnv* env, jobject clazz, jint handle, jobject channel)
947{
948    LOG_TRACE("onInputChannelDestroyed_native");
949    if (handle != 0) {
950        NativeCode* code = (NativeCode*)handle;
951        if (code->nativeInputQueue != NULL
952                && code->callbacks.onInputQueueDestroyed != NULL) {
953            code->callbacks.onInputQueueDestroyed(code,
954                    code->nativeInputQueue);
955        }
956        code->setInputChannel(NULL);
957    }
958}
959
960static void
961onContentRectChanged_native(JNIEnv* env, jobject clazz, jint handle,
962        jint x, jint y, jint w, jint h)
963{
964    LOG_TRACE("onContentRectChanged_native");
965    if (handle != 0) {
966        NativeCode* code = (NativeCode*)handle;
967        if (code->callbacks.onContentRectChanged != NULL) {
968            ARect rect;
969            rect.left = x;
970            rect.top = y;
971            rect.right = x+w;
972            rect.bottom = y+h;
973            code->callbacks.onContentRectChanged(code, &rect);
974        }
975    }
976}
977
978static void
979dispatchKeyEvent_native(JNIEnv* env, jobject clazz, jint handle, jobject eventObj)
980{
981    LOG_TRACE("dispatchKeyEvent_native");
982    if (handle != 0) {
983        NativeCode* code = (NativeCode*)handle;
984        if (code->nativeInputQueue != NULL) {
985            KeyEvent* event = code->nativeInputQueue->createKeyEvent();
986            android_view_KeyEvent_toNative(env, eventObj, event);
987            code->nativeInputQueue->dispatchEvent(event);
988        }
989    }
990}
991
992static void
993finishPreDispatchKeyEvent_native(JNIEnv* env, jobject clazz, jint handle,
994        jint seq, jboolean handled)
995{
996    LOG_TRACE("finishPreDispatchKeyEvent_native");
997    if (handle != 0) {
998        NativeCode* code = (NativeCode*)handle;
999        if (code->nativeInputQueue != NULL) {
1000            code->nativeInputQueue->finishPreDispatch(seq, handled ? true : false);
1001        }
1002    }
1003}
1004
1005static const JNINativeMethod g_methods[] = {
1006    { "loadNativeCode", "(Ljava/lang/String;Ljava/lang/String;Landroid/os/MessageQueue;Ljava/lang/String;Ljava/lang/String;ILandroid/content/res/AssetManager;[B)I",
1007            (void*)loadNativeCode_native },
1008    { "unloadNativeCode", "(I)V", (void*)unloadNativeCode_native },
1009    { "onStartNative", "(I)V", (void*)onStart_native },
1010    { "onResumeNative", "(I)V", (void*)onResume_native },
1011    { "onSaveInstanceStateNative", "(I)[B", (void*)onSaveInstanceState_native },
1012    { "onPauseNative", "(I)V", (void*)onPause_native },
1013    { "onStopNative", "(I)V", (void*)onStop_native },
1014    { "onConfigurationChangedNative", "(I)V", (void*)onConfigurationChanged_native },
1015    { "onLowMemoryNative", "(I)V", (void*)onLowMemory_native },
1016    { "onWindowFocusChangedNative", "(IZ)V", (void*)onWindowFocusChanged_native },
1017    { "onSurfaceCreatedNative", "(ILandroid/view/Surface;)V", (void*)onSurfaceCreated_native },
1018    { "onSurfaceChangedNative", "(ILandroid/view/Surface;III)V", (void*)onSurfaceChanged_native },
1019    { "onSurfaceRedrawNeededNative", "(ILandroid/view/Surface;)V", (void*)onSurfaceRedrawNeeded_native },
1020    { "onSurfaceDestroyedNative", "(I)V", (void*)onSurfaceDestroyed_native },
1021    { "onInputChannelCreatedNative", "(ILandroid/view/InputChannel;)V", (void*)onInputChannelCreated_native },
1022    { "onInputChannelDestroyedNative", "(ILandroid/view/InputChannel;)V", (void*)onInputChannelDestroyed_native },
1023    { "onContentRectChangedNative", "(IIIII)V", (void*)onContentRectChanged_native },
1024    { "dispatchKeyEventNative", "(ILandroid/view/KeyEvent;)V", (void*)dispatchKeyEvent_native },
1025    { "finishPreDispatchKeyEventNative", "(IIZ)V", (void*)finishPreDispatchKeyEvent_native },
1026};
1027
1028static const char* const kNativeActivityPathName = "android/app/NativeActivity";
1029
1030#define FIND_CLASS(var, className) \
1031        var = env->FindClass(className); \
1032        LOG_FATAL_IF(! var, "Unable to find class " className); \
1033        var = jclass(env->NewGlobalRef(var));
1034
1035#define GET_METHOD_ID(var, clazz, methodName, fieldDescriptor) \
1036        var = env->GetMethodID(clazz, methodName, fieldDescriptor); \
1037        LOG_FATAL_IF(! var, "Unable to find method" methodName);
1038
1039int register_android_app_NativeActivity(JNIEnv* env)
1040{
1041    //LOGD("register_android_app_NativeActivity");
1042
1043    FIND_CLASS(gNativeActivityClassInfo.clazz, kNativeActivityPathName);
1044
1045    GET_METHOD_ID(gNativeActivityClassInfo.dispatchUnhandledKeyEvent,
1046            gNativeActivityClassInfo.clazz,
1047            "dispatchUnhandledKeyEvent", "(Landroid/view/KeyEvent;)V");
1048    GET_METHOD_ID(gNativeActivityClassInfo.preDispatchKeyEvent,
1049            gNativeActivityClassInfo.clazz,
1050            "preDispatchKeyEvent", "(Landroid/view/KeyEvent;I)V");
1051
1052    GET_METHOD_ID(gNativeActivityClassInfo.finish,
1053            gNativeActivityClassInfo.clazz,
1054            "finish", "()V");
1055    GET_METHOD_ID(gNativeActivityClassInfo.setWindowFlags,
1056            gNativeActivityClassInfo.clazz,
1057            "setWindowFlags", "(II)V");
1058    GET_METHOD_ID(gNativeActivityClassInfo.setWindowFormat,
1059            gNativeActivityClassInfo.clazz,
1060            "setWindowFormat", "(I)V");
1061    GET_METHOD_ID(gNativeActivityClassInfo.showIme,
1062            gNativeActivityClassInfo.clazz,
1063            "showIme", "(I)V");
1064    GET_METHOD_ID(gNativeActivityClassInfo.hideIme,
1065            gNativeActivityClassInfo.clazz,
1066            "hideIme", "(I)V");
1067
1068    return AndroidRuntime::registerNativeMethods(
1069        env, kNativeActivityPathName,
1070        g_methods, NELEM(g_methods));
1071}
1072
1073} // namespace android
1074