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