android_app_NativeActivity.cpp revision 17cc33a35729733aaa0a7706f38b1c45f0b1590a
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    jmethodID dispatchUnhandledKeyEvent;
46    jmethodID preDispatchKeyEvent;
47    jmethodID finish;
48    jmethodID setWindowFlags;
49    jmethodID setWindowFormat;
50    jmethodID showIme;
51    jmethodID hideIme;
52} gNativeActivityClassInfo;
53
54// ------------------------------------------------------------------------
55
56struct ActivityWork {
57    int32_t cmd;
58    int32_t arg1;
59    int32_t arg2;
60};
61
62enum {
63    CMD_DEF_KEY = 1,
64    CMD_FINISH,
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    mLooper = static_cast<android::Looper*>(looper);
133    mLooper->addFd(mConsumer.getChannel()->getReceivePipeFd(),
134            ident, ALOOPER_EVENT_INPUT, callback, data);
135    mLooper->addFd(mDispatchKeyRead,
136            ident, ALOOPER_EVENT_INPUT, callback, data);
137}
138
139void AInputQueue::detachLooper() {
140    mLooper->removeFd(mConsumer.getChannel()->getReceivePipeFd());
141    mLooper->removeFd(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[1].events = POLLIN;
152    pfd[1].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, false);
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(false);
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, bool didDefaultHandling) {
247    LOG_TRACE("finishEvent: %p handled=%d, didDefaultHandling=%d", event,
248            handled ? 1 : 0, didDefaultHandling ? 1 : 0);
249
250    if (!handled && !didDefaultHandling
251            && ((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(handled);
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 internalDataPathObj;
502    String8 externalDataPathObj;
503    String8 obbPathObj;
504
505    sp<ANativeWindow> nativeWindow;
506    int32_t lastWindowWidth;
507    int32_t lastWindowHeight;
508
509    jobject inputChannel;
510    struct AInputQueue* nativeInputQueue;
511
512    // These are used to wake up the main thread to process work.
513    int mainWorkRead;
514    int mainWorkWrite;
515    sp<Looper> looper;
516};
517
518void android_NativeActivity_finish(ANativeActivity* activity) {
519    NativeCode* code = static_cast<NativeCode*>(activity);
520    write_work(code->mainWorkWrite, CMD_FINISH, 0);
521}
522
523void android_NativeActivity_setWindowFormat(
524        ANativeActivity* activity, int32_t format) {
525    NativeCode* code = static_cast<NativeCode*>(activity);
526    write_work(code->mainWorkWrite, CMD_SET_WINDOW_FORMAT, format);
527}
528
529void android_NativeActivity_setWindowFlags(
530        ANativeActivity* activity, int32_t values, int32_t mask) {
531    NativeCode* code = static_cast<NativeCode*>(activity);
532    write_work(code->mainWorkWrite, CMD_SET_WINDOW_FLAGS, values, mask);
533}
534
535void android_NativeActivity_showSoftInput(
536        ANativeActivity* activity, int32_t flags) {
537    NativeCode* code = static_cast<NativeCode*>(activity);
538    write_work(code->mainWorkWrite, CMD_SHOW_SOFT_INPUT, flags);
539}
540
541void android_NativeActivity_hideSoftInput(
542        ANativeActivity* activity, int32_t flags) {
543    NativeCode* code = static_cast<NativeCode*>(activity);
544    write_work(code->mainWorkWrite, CMD_HIDE_SOFT_INPUT, flags);
545}
546
547// ------------------------------------------------------------------------
548
549static bool checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
550   if (env->ExceptionCheck()) {
551       LOGE("An exception was thrown by callback '%s'.", methodName);
552       LOGE_EX(env);
553       env->ExceptionClear();
554       return true;
555   }
556   return false;
557}
558
559/*
560 * Callback for handling native events on the application's main thread.
561 */
562static int mainWorkCallback(int fd, int events, void* data) {
563    NativeCode* code = (NativeCode*)data;
564    if ((events & POLLIN) == 0) {
565        return 1;
566    }
567
568    ActivityWork work;
569    if (!read_work(code->mainWorkRead, &work)) {
570        return 1;
571    }
572
573    LOG_TRACE("mainWorkCallback: cmd=%d", work.cmd);
574
575    switch (work.cmd) {
576        case CMD_DEF_KEY: {
577            KeyEvent* keyEvent;
578            while ((keyEvent=code->nativeInputQueue->consumeUnhandledEvent()) != NULL) {
579                jobject inputEventObj = android_view_KeyEvent_fromNative(
580                        code->env, keyEvent);
581                jboolean handled;
582                if (inputEventObj) {
583                    handled = code->env->CallBooleanMethod(code->clazz,
584                            gNativeActivityClassInfo.dispatchUnhandledKeyEvent, inputEventObj);
585                    checkAndClearExceptionFromCallback(code->env, "dispatchUnhandledKeyEvent");
586                    code->env->DeleteLocalRef(inputEventObj);
587                } else {
588                    LOGE("Failed to obtain key event for dispatchUnhandledKeyEvent.");
589                    handled = false;
590                }
591                code->nativeInputQueue->finishEvent(keyEvent, handled, true);
592            }
593            int seq;
594            while ((keyEvent=code->nativeInputQueue->consumePreDispatchingEvent(&seq)) != NULL) {
595                jobject inputEventObj = android_view_KeyEvent_fromNative(
596                        code->env, keyEvent);
597                if (inputEventObj) {
598                    code->env->CallVoidMethod(code->clazz,
599                            gNativeActivityClassInfo.preDispatchKeyEvent, inputEventObj, seq);
600                    checkAndClearExceptionFromCallback(code->env, "preDispatchKeyEvent");
601                    code->env->DeleteLocalRef(inputEventObj);
602                } else {
603                    LOGE("Failed to obtain key event for preDispatchKeyEvent.");
604                }
605            }
606        } break;
607        case CMD_FINISH: {
608            code->env->CallVoidMethod(code->clazz, gNativeActivityClassInfo.finish);
609            checkAndClearExceptionFromCallback(code->env, "finish");
610        } break;
611        case CMD_SET_WINDOW_FORMAT: {
612            code->env->CallVoidMethod(code->clazz,
613                    gNativeActivityClassInfo.setWindowFormat, work.arg1);
614            checkAndClearExceptionFromCallback(code->env, "setWindowFormat");
615        } break;
616        case CMD_SET_WINDOW_FLAGS: {
617            code->env->CallVoidMethod(code->clazz,
618                    gNativeActivityClassInfo.setWindowFlags, work.arg1, work.arg2);
619            checkAndClearExceptionFromCallback(code->env, "setWindowFlags");
620        } break;
621        case CMD_SHOW_SOFT_INPUT: {
622            code->env->CallVoidMethod(code->clazz,
623                    gNativeActivityClassInfo.showIme, work.arg1);
624            checkAndClearExceptionFromCallback(code->env, "showIme");
625        } break;
626        case CMD_HIDE_SOFT_INPUT: {
627            code->env->CallVoidMethod(code->clazz,
628                    gNativeActivityClassInfo.hideIme, work.arg1);
629            checkAndClearExceptionFromCallback(code->env, "hideIme");
630        } break;
631        default:
632            LOGW("Unknown work command: %d", work.cmd);
633            break;
634    }
635
636    return 1;
637}
638
639// ------------------------------------------------------------------------
640
641static jint
642loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path, jstring funcName,
643        jobject messageQueue, jstring internalDataDir, jstring obbDir,
644        jstring externalDataDir, int sdkVersion,
645        jobject jAssetMgr, jbyteArray savedState)
646{
647    LOG_TRACE("loadNativeCode_native");
648
649    const char* pathStr = env->GetStringUTFChars(path, NULL);
650    NativeCode* code = NULL;
651
652    void* handle = dlopen(pathStr, RTLD_LAZY);
653
654    env->ReleaseStringUTFChars(path, pathStr);
655
656    if (handle != NULL) {
657        const char* funcStr = env->GetStringUTFChars(funcName, NULL);
658        code = new NativeCode(handle, (ANativeActivity_createFunc*)
659                dlsym(handle, funcStr));
660        env->ReleaseStringUTFChars(funcName, funcStr);
661
662        if (code->createActivityFunc == NULL) {
663            LOGW("ANativeActivity_onCreate not found");
664            delete code;
665            return 0;
666        }
667
668        code->looper = android_os_MessageQueue_getLooper(env, messageQueue);
669        if (code->looper == NULL) {
670            LOGW("Unable to retrieve MessageQueue's Looper");
671            delete code;
672            return 0;
673        }
674
675        int msgpipe[2];
676        if (pipe(msgpipe)) {
677            LOGW("could not create pipe: %s", strerror(errno));
678            delete code;
679            return 0;
680        }
681        code->mainWorkRead = msgpipe[0];
682        code->mainWorkWrite = msgpipe[1];
683        int result = fcntl(code->mainWorkRead, F_SETFL, O_NONBLOCK);
684        SLOGW_IF(result != 0, "Could not make main work read pipe "
685                "non-blocking: %s", strerror(errno));
686        result = fcntl(code->mainWorkWrite, F_SETFL, O_NONBLOCK);
687        SLOGW_IF(result != 0, "Could not make main work write pipe "
688                "non-blocking: %s", strerror(errno));
689        code->looper->addFd(code->mainWorkRead, 0, ALOOPER_EVENT_INPUT, mainWorkCallback, code);
690
691        code->ANativeActivity::callbacks = &code->callbacks;
692        if (env->GetJavaVM(&code->vm) < 0) {
693            LOGW("NativeActivity GetJavaVM failed");
694            delete code;
695            return 0;
696        }
697        code->env = env;
698        code->clazz = env->NewGlobalRef(clazz);
699
700        const char* dirStr = env->GetStringUTFChars(internalDataDir, NULL);
701        code->internalDataPathObj = dirStr;
702        code->internalDataPath = code->internalDataPathObj.string();
703        env->ReleaseStringUTFChars(internalDataDir, dirStr);
704
705        dirStr = env->GetStringUTFChars(externalDataDir, NULL);
706        code->externalDataPathObj = dirStr;
707        code->externalDataPath = code->externalDataPathObj.string();
708        env->ReleaseStringUTFChars(externalDataDir, dirStr);
709
710        code->sdkVersion = sdkVersion;
711
712        code->assetManager = assetManagerForJavaObject(env, jAssetMgr);
713
714        dirStr = env->GetStringUTFChars(obbDir, NULL);
715        code->obbPathObj = dirStr;
716        code->obbPath = code->obbPathObj.string();
717        env->ReleaseStringUTFChars(obbDir, dirStr);
718
719        jbyte* rawSavedState = NULL;
720        jsize rawSavedSize = 0;
721        if (savedState != NULL) {
722            rawSavedState = env->GetByteArrayElements(savedState, NULL);
723            rawSavedSize = env->GetArrayLength(savedState);
724        }
725
726        code->createActivityFunc(code, rawSavedState, rawSavedSize);
727
728        if (rawSavedState != NULL) {
729            env->ReleaseByteArrayElements(savedState, rawSavedState, 0);
730        }
731    }
732
733    return (jint)code;
734}
735
736static void
737unloadNativeCode_native(JNIEnv* env, jobject clazz, jint handle)
738{
739    LOG_TRACE("unloadNativeCode_native");
740    if (handle != 0) {
741        NativeCode* code = (NativeCode*)handle;
742        delete code;
743    }
744}
745
746static void
747onStart_native(JNIEnv* env, jobject clazz, jint handle)
748{
749    LOG_TRACE("onStart_native");
750    if (handle != 0) {
751        NativeCode* code = (NativeCode*)handle;
752        if (code->callbacks.onStart != NULL) {
753            code->callbacks.onStart(code);
754        }
755    }
756}
757
758static void
759onResume_native(JNIEnv* env, jobject clazz, jint handle)
760{
761    LOG_TRACE("onResume_native");
762    if (handle != 0) {
763        NativeCode* code = (NativeCode*)handle;
764        if (code->callbacks.onResume != NULL) {
765            code->callbacks.onResume(code);
766        }
767    }
768}
769
770static jbyteArray
771onSaveInstanceState_native(JNIEnv* env, jobject clazz, jint handle)
772{
773    LOG_TRACE("onSaveInstanceState_native");
774
775    jbyteArray array = NULL;
776
777    if (handle != 0) {
778        NativeCode* code = (NativeCode*)handle;
779        if (code->callbacks.onSaveInstanceState != NULL) {
780            size_t len = 0;
781            jbyte* state = (jbyte*)code->callbacks.onSaveInstanceState(code, &len);
782            if (len > 0) {
783                array = env->NewByteArray(len);
784                if (array != NULL) {
785                    env->SetByteArrayRegion(array, 0, len, state);
786                }
787            }
788            if (state != NULL) {
789                free(state);
790            }
791        }
792    }
793
794    return array;
795}
796
797static void
798onPause_native(JNIEnv* env, jobject clazz, jint handle)
799{
800    LOG_TRACE("onPause_native");
801    if (handle != 0) {
802        NativeCode* code = (NativeCode*)handle;
803        if (code->callbacks.onPause != NULL) {
804            code->callbacks.onPause(code);
805        }
806    }
807}
808
809static void
810onStop_native(JNIEnv* env, jobject clazz, jint handle)
811{
812    LOG_TRACE("onStop_native");
813    if (handle != 0) {
814        NativeCode* code = (NativeCode*)handle;
815        if (code->callbacks.onStop != NULL) {
816            code->callbacks.onStop(code);
817        }
818    }
819}
820
821static void
822onConfigurationChanged_native(JNIEnv* env, jobject clazz, jint handle)
823{
824    LOG_TRACE("onConfigurationChanged_native");
825    if (handle != 0) {
826        NativeCode* code = (NativeCode*)handle;
827        if (code->callbacks.onConfigurationChanged != NULL) {
828            code->callbacks.onConfigurationChanged(code);
829        }
830    }
831}
832
833static void
834onLowMemory_native(JNIEnv* env, jobject clazz, jint handle)
835{
836    LOG_TRACE("onLowMemory_native");
837    if (handle != 0) {
838        NativeCode* code = (NativeCode*)handle;
839        if (code->callbacks.onLowMemory != NULL) {
840            code->callbacks.onLowMemory(code);
841        }
842    }
843}
844
845static void
846onWindowFocusChanged_native(JNIEnv* env, jobject clazz, jint handle, jboolean focused)
847{
848    LOG_TRACE("onWindowFocusChanged_native");
849    if (handle != 0) {
850        NativeCode* code = (NativeCode*)handle;
851        if (code->callbacks.onWindowFocusChanged != NULL) {
852            code->callbacks.onWindowFocusChanged(code, focused ? 1 : 0);
853        }
854    }
855}
856
857static void
858onSurfaceCreated_native(JNIEnv* env, jobject clazz, jint handle, jobject surface)
859{
860    LOG_TRACE("onSurfaceCreated_native");
861    if (handle != 0) {
862        NativeCode* code = (NativeCode*)handle;
863        code->setSurface(surface);
864        if (code->nativeWindow != NULL && code->callbacks.onNativeWindowCreated != NULL) {
865            code->callbacks.onNativeWindowCreated(code,
866                    code->nativeWindow.get());
867        }
868    }
869}
870
871static int32_t getWindowProp(ANativeWindow* window, int what) {
872    int value;
873    int res = window->query(window, what, &value);
874    return res < 0 ? res : value;
875}
876
877static void
878onSurfaceChanged_native(JNIEnv* env, jobject clazz, jint handle, jobject surface,
879        jint format, jint width, jint height)
880{
881    LOG_TRACE("onSurfaceChanged_native");
882    if (handle != 0) {
883        NativeCode* code = (NativeCode*)handle;
884        sp<ANativeWindow> oldNativeWindow = code->nativeWindow;
885        code->setSurface(surface);
886        if (oldNativeWindow != code->nativeWindow) {
887            if (oldNativeWindow != NULL && code->callbacks.onNativeWindowDestroyed != NULL) {
888                code->callbacks.onNativeWindowDestroyed(code,
889                        oldNativeWindow.get());
890            }
891            if (code->nativeWindow != NULL) {
892                if (code->callbacks.onNativeWindowCreated != NULL) {
893                    code->callbacks.onNativeWindowCreated(code,
894                            code->nativeWindow.get());
895                }
896                code->lastWindowWidth = getWindowProp(code->nativeWindow.get(),
897                        NATIVE_WINDOW_WIDTH);
898                code->lastWindowHeight = getWindowProp(code->nativeWindow.get(),
899                        NATIVE_WINDOW_HEIGHT);
900            }
901        } else {
902            // Maybe it resized?
903            int32_t newWidth = getWindowProp(code->nativeWindow.get(),
904                    NATIVE_WINDOW_WIDTH);
905            int32_t newHeight = getWindowProp(code->nativeWindow.get(),
906                    NATIVE_WINDOW_HEIGHT);
907            if (newWidth != code->lastWindowWidth
908                    || newHeight != code->lastWindowHeight) {
909                if (code->callbacks.onNativeWindowResized != NULL) {
910                    code->callbacks.onNativeWindowResized(code,
911                            code->nativeWindow.get());
912                }
913            }
914        }
915    }
916}
917
918static void
919onSurfaceRedrawNeeded_native(JNIEnv* env, jobject clazz, jint handle)
920{
921    LOG_TRACE("onSurfaceRedrawNeeded_native");
922    if (handle != 0) {
923        NativeCode* code = (NativeCode*)handle;
924        if (code->nativeWindow != NULL && code->callbacks.onNativeWindowRedrawNeeded != NULL) {
925            code->callbacks.onNativeWindowRedrawNeeded(code, code->nativeWindow.get());
926        }
927    }
928}
929
930static void
931onSurfaceDestroyed_native(JNIEnv* env, jobject clazz, jint handle, jobject surface)
932{
933    LOG_TRACE("onSurfaceDestroyed_native");
934    if (handle != 0) {
935        NativeCode* code = (NativeCode*)handle;
936        if (code->nativeWindow != NULL && code->callbacks.onNativeWindowDestroyed != NULL) {
937            code->callbacks.onNativeWindowDestroyed(code,
938                    code->nativeWindow.get());
939        }
940        code->setSurface(NULL);
941    }
942}
943
944static void
945onInputChannelCreated_native(JNIEnv* env, jobject clazz, jint handle, jobject channel)
946{
947    LOG_TRACE("onInputChannelCreated_native");
948    if (handle != 0) {
949        NativeCode* code = (NativeCode*)handle;
950        status_t err = code->setInputChannel(channel);
951        if (err != OK) {
952            jniThrowException(env, "java/lang/IllegalStateException",
953                    "Error setting input channel");
954            return;
955        }
956        if (code->callbacks.onInputQueueCreated != NULL) {
957            code->callbacks.onInputQueueCreated(code,
958                    code->nativeInputQueue);
959        }
960    }
961}
962
963static void
964onInputChannelDestroyed_native(JNIEnv* env, jobject clazz, jint handle, jobject channel)
965{
966    LOG_TRACE("onInputChannelDestroyed_native");
967    if (handle != 0) {
968        NativeCode* code = (NativeCode*)handle;
969        if (code->nativeInputQueue != NULL
970                && code->callbacks.onInputQueueDestroyed != NULL) {
971            code->callbacks.onInputQueueDestroyed(code,
972                    code->nativeInputQueue);
973        }
974        code->setInputChannel(NULL);
975    }
976}
977
978static void
979onContentRectChanged_native(JNIEnv* env, jobject clazz, jint handle,
980        jint x, jint y, jint w, jint h)
981{
982    LOG_TRACE("onContentRectChanged_native");
983    if (handle != 0) {
984        NativeCode* code = (NativeCode*)handle;
985        if (code->callbacks.onContentRectChanged != NULL) {
986            ARect rect;
987            rect.left = x;
988            rect.top = y;
989            rect.right = x+w;
990            rect.bottom = y+h;
991            code->callbacks.onContentRectChanged(code, &rect);
992        }
993    }
994}
995
996static void
997dispatchKeyEvent_native(JNIEnv* env, jobject clazz, jint handle, jobject eventObj)
998{
999    LOG_TRACE("dispatchKeyEvent_native");
1000    if (handle != 0) {
1001        NativeCode* code = (NativeCode*)handle;
1002        if (code->nativeInputQueue != NULL) {
1003            KeyEvent* event = code->nativeInputQueue->createKeyEvent();
1004            status_t status = android_view_KeyEvent_toNative(env, eventObj, event);
1005            if (status) {
1006                delete event;
1007                jniThrowRuntimeException(env, "Could not read contents of KeyEvent object.");
1008                return;
1009            }
1010            code->nativeInputQueue->dispatchEvent(event);
1011        }
1012    }
1013}
1014
1015static void
1016finishPreDispatchKeyEvent_native(JNIEnv* env, jobject clazz, jint handle,
1017        jint seq, jboolean handled)
1018{
1019    LOG_TRACE("finishPreDispatchKeyEvent_native");
1020    if (handle != 0) {
1021        NativeCode* code = (NativeCode*)handle;
1022        if (code->nativeInputQueue != NULL) {
1023            code->nativeInputQueue->finishPreDispatch(seq, handled ? true : false);
1024        }
1025    }
1026}
1027
1028static const JNINativeMethod g_methods[] = {
1029    { "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",
1030            (void*)loadNativeCode_native },
1031    { "unloadNativeCode", "(I)V", (void*)unloadNativeCode_native },
1032    { "onStartNative", "(I)V", (void*)onStart_native },
1033    { "onResumeNative", "(I)V", (void*)onResume_native },
1034    { "onSaveInstanceStateNative", "(I)[B", (void*)onSaveInstanceState_native },
1035    { "onPauseNative", "(I)V", (void*)onPause_native },
1036    { "onStopNative", "(I)V", (void*)onStop_native },
1037    { "onConfigurationChangedNative", "(I)V", (void*)onConfigurationChanged_native },
1038    { "onLowMemoryNative", "(I)V", (void*)onLowMemory_native },
1039    { "onWindowFocusChangedNative", "(IZ)V", (void*)onWindowFocusChanged_native },
1040    { "onSurfaceCreatedNative", "(ILandroid/view/Surface;)V", (void*)onSurfaceCreated_native },
1041    { "onSurfaceChangedNative", "(ILandroid/view/Surface;III)V", (void*)onSurfaceChanged_native },
1042    { "onSurfaceRedrawNeededNative", "(ILandroid/view/Surface;)V", (void*)onSurfaceRedrawNeeded_native },
1043    { "onSurfaceDestroyedNative", "(I)V", (void*)onSurfaceDestroyed_native },
1044    { "onInputChannelCreatedNative", "(ILandroid/view/InputChannel;)V", (void*)onInputChannelCreated_native },
1045    { "onInputChannelDestroyedNative", "(ILandroid/view/InputChannel;)V", (void*)onInputChannelDestroyed_native },
1046    { "onContentRectChangedNative", "(IIIII)V", (void*)onContentRectChanged_native },
1047    { "dispatchKeyEventNative", "(ILandroid/view/KeyEvent;)V", (void*)dispatchKeyEvent_native },
1048    { "finishPreDispatchKeyEventNative", "(IIZ)V", (void*)finishPreDispatchKeyEvent_native },
1049};
1050
1051static const char* const kNativeActivityPathName = "android/app/NativeActivity";
1052
1053#define FIND_CLASS(var, className) \
1054        var = env->FindClass(className); \
1055        LOG_FATAL_IF(! var, "Unable to find class %s", className);
1056
1057#define GET_METHOD_ID(var, clazz, methodName, fieldDescriptor) \
1058        var = env->GetMethodID(clazz, methodName, fieldDescriptor); \
1059        LOG_FATAL_IF(! var, "Unable to find method" methodName);
1060
1061int register_android_app_NativeActivity(JNIEnv* env)
1062{
1063    //LOGD("register_android_app_NativeActivity");
1064    jclass clazz;
1065    FIND_CLASS(clazz, kNativeActivityPathName);
1066
1067    GET_METHOD_ID(gNativeActivityClassInfo.dispatchUnhandledKeyEvent,
1068            clazz,
1069            "dispatchUnhandledKeyEvent", "(Landroid/view/KeyEvent;)Z");
1070    GET_METHOD_ID(gNativeActivityClassInfo.preDispatchKeyEvent,
1071            clazz,
1072            "preDispatchKeyEvent", "(Landroid/view/KeyEvent;I)V");
1073
1074    GET_METHOD_ID(gNativeActivityClassInfo.finish,
1075            clazz,
1076            "finish", "()V");
1077    GET_METHOD_ID(gNativeActivityClassInfo.setWindowFlags,
1078            clazz,
1079            "setWindowFlags", "(II)V");
1080    GET_METHOD_ID(gNativeActivityClassInfo.setWindowFormat,
1081            clazz,
1082            "setWindowFormat", "(I)V");
1083    GET_METHOD_ID(gNativeActivityClassInfo.showIme,
1084            clazz,
1085            "showIme", "(I)V");
1086    GET_METHOD_ID(gNativeActivityClassInfo.hideIme,
1087            clazz,
1088            "hideIme", "(I)V");
1089
1090    return AndroidRuntime::registerNativeMethods(
1091        env, kNativeActivityPathName,
1092        g_methods, NELEM(g_methods));
1093}
1094
1095} // namespace android
1096