android_app_NativeActivity.cpp revision 8335f1ccccedb6655d96d9d5b697a7f0938235dd
1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_TAG "NativeActivity"
18#include <utils/Log.h>
19
20#include <poll.h>
21#include <dlfcn.h>
22#include <fcntl.h>
23
24#include <android_runtime/android_app_NativeActivity.h>
25#include <android_runtime/android_util_AssetManager.h>
26#include <android_runtime/android_view_Surface.h>
27#include <android_runtime/AndroidRuntime.h>
28#include <androidfw/InputTransport.h>
29
30#include <gui/Surface.h>
31
32#include <system/window.h>
33
34#include <utils/Looper.h>
35
36#include "JNIHelp.h"
37#include "android_os_MessageQueue.h"
38#include "android_view_InputChannel.h"
39#include "android_view_KeyEvent.h"
40
41#define LOG_TRACE(...)
42//#define LOG_TRACE(...) ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)
43
44namespace android
45{
46
47static struct {
48    jmethodID dispatchUnhandledKeyEvent;
49    jmethodID preDispatchKeyEvent;
50    jmethodID finish;
51    jmethodID setWindowFlags;
52    jmethodID setWindowFormat;
53    jmethodID showIme;
54    jmethodID hideIme;
55} gNativeActivityClassInfo;
56
57// ------------------------------------------------------------------------
58
59struct ActivityWork {
60    int32_t cmd;
61    int32_t arg1;
62    int32_t arg2;
63};
64
65enum {
66    CMD_DEF_KEY = 1,
67    CMD_FINISH,
68    CMD_SET_WINDOW_FORMAT,
69    CMD_SET_WINDOW_FLAGS,
70    CMD_SHOW_SOFT_INPUT,
71    CMD_HIDE_SOFT_INPUT,
72};
73
74static void write_work(int fd, int32_t cmd, int32_t arg1=0, int32_t arg2=0) {
75    ActivityWork work;
76    work.cmd = cmd;
77    work.arg1 = arg1;
78    work.arg2 = arg2;
79
80    LOG_TRACE("write_work: cmd=%d", cmd);
81
82restart:
83    int res = write(fd, &work, sizeof(work));
84    if (res < 0 && errno == EINTR) {
85        goto restart;
86    }
87
88    if (res == sizeof(work)) return;
89
90    if (res < 0) ALOGW("Failed writing to work fd: %s", strerror(errno));
91    else ALOGW("Truncated writing to work fd: %d", res);
92}
93
94static bool read_work(int fd, ActivityWork* outWork) {
95    int res = read(fd, outWork, sizeof(ActivityWork));
96    // no need to worry about EINTR, poll loop will just come back again.
97    if (res == sizeof(ActivityWork)) return true;
98
99    if (res < 0) ALOGW("Failed reading work fd: %s", strerror(errno));
100    else ALOGW("Truncated reading work fd: %d", res);
101    return false;
102}
103
104// ------------------------------------------------------------------------
105
106} // namespace android
107
108using namespace android;
109
110AInputQueue::AInputQueue(const sp<InputChannel>& channel, int workWrite) :
111        mWorkWrite(workWrite), mConsumer(channel), mSeq(0) {
112    int msgpipe[2];
113    if (pipe(msgpipe)) {
114        ALOGW("could not create pipe: %s", strerror(errno));
115        mDispatchKeyRead = mDispatchKeyWrite = -1;
116    } else {
117        mDispatchKeyRead = msgpipe[0];
118        mDispatchKeyWrite = msgpipe[1];
119        int result = fcntl(mDispatchKeyRead, F_SETFL, O_NONBLOCK);
120        SLOGW_IF(result != 0, "Could not make AInputQueue read pipe "
121                "non-blocking: %s", strerror(errno));
122        result = fcntl(mDispatchKeyWrite, F_SETFL, O_NONBLOCK);
123        SLOGW_IF(result != 0, "Could not make AInputQueue write pipe "
124                "non-blocking: %s", strerror(errno));
125    }
126}
127
128AInputQueue::~AInputQueue() {
129    close(mDispatchKeyRead);
130    close(mDispatchKeyWrite);
131}
132
133void AInputQueue::attachLooper(ALooper* looper, int ident,
134        ALooper_callbackFunc callback, void* data) {
135    mLooper = static_cast<android::Looper*>(looper);
136    mLooper->addFd(mConsumer.getChannel()->getFd(),
137            ident, ALOOPER_EVENT_INPUT, callback, data);
138    mLooper->addFd(mDispatchKeyRead,
139            ident, ALOOPER_EVENT_INPUT, callback, data);
140}
141
142void AInputQueue::detachLooper() {
143    mLooper->removeFd(mConsumer.getChannel()->getFd());
144    mLooper->removeFd(mDispatchKeyRead);
145}
146
147int32_t AInputQueue::hasEvents() {
148    struct pollfd pfd[2];
149
150    pfd[0].fd = mConsumer.getChannel()->getFd();
151    pfd[0].events = POLLIN;
152    pfd[0].revents = 0;
153    pfd[1].fd = mDispatchKeyRead;
154    pfd[1].events = POLLIN;
155    pfd[1].revents = 0;
156
157    int nfd = poll(pfd, 2, 0);
158    if (nfd <= 0) return 0;
159    return ((pfd[0].revents & POLLIN) || (pfd[1].revents & POLLIN)) ? 1 : -1;
160}
161
162int32_t AInputQueue::getEvent(AInputEvent** outEvent) {
163    *outEvent = NULL;
164
165    bool finishNow = false;
166
167    char byteread;
168    ssize_t nRead = read(mDispatchKeyRead, &byteread, 1);
169    if (nRead == 1) {
170        mLock.lock();
171        if (mDispatchingKeys.size() > 0) {
172            KeyEvent* kevent = mDispatchingKeys[0];
173            *outEvent = kevent;
174            mDispatchingKeys.removeAt(0);
175            in_flight_event inflight;
176            inflight.event = kevent;
177            inflight.seq = -1;
178            inflight.finishSeq = 0;
179            mInFlightEvents.push(inflight);
180        }
181        if (mFinishPreDispatches.size() > 0) {
182            finish_pre_dispatch finish(mFinishPreDispatches[0]);
183            mFinishPreDispatches.removeAt(0);
184            const size_t N = mInFlightEvents.size();
185            for (size_t i=0; i<N; i++) {
186                const in_flight_event& inflight(mInFlightEvents[i]);
187                if (inflight.seq == finish.seq) {
188                    *outEvent = inflight.event;
189                    finishNow = finish.handled;
190                }
191            }
192            if (*outEvent == NULL) {
193                ALOGW("getEvent couldn't find inflight for seq %d", finish.seq);
194            }
195        }
196        mLock.unlock();
197
198        if (finishNow) {
199            finishEvent(*outEvent, true, false);
200            *outEvent = NULL;
201            return -1;
202        } else if (*outEvent != NULL) {
203            return 0;
204        }
205    }
206
207    uint32_t consumerSeq;
208    InputEvent* myEvent = NULL;
209    status_t res = mConsumer.consume(this, true /*consumeBatches*/, &consumerSeq, &myEvent);
210    if (res != android::OK) {
211        if (res != android::WOULD_BLOCK) {
212            ALOGW("channel '%s' ~ Failed to consume input event.  status=%d",
213                    mConsumer.getChannel()->getName().string(), res);
214        }
215        return -1;
216    }
217
218    in_flight_event inflight;
219    inflight.event = myEvent;
220    inflight.seq = -1;
221    inflight.finishSeq = consumerSeq;
222    mInFlightEvents.push(inflight);
223
224    *outEvent = myEvent;
225    return 0;
226}
227
228bool AInputQueue::preDispatchEvent(AInputEvent* event) {
229    if (((InputEvent*)event)->getType() != AINPUT_EVENT_TYPE_KEY) {
230        // The IME only cares about key events.
231        return false;
232    }
233
234    // For now we only send system keys to the IME...  this avoids having
235    // critical keys like DPAD go through this path.  We really need to have
236    // the IME report which keys it wants.
237    if (!((KeyEvent*)event)->isSystemKey()) {
238        return false;
239    }
240
241    return preDispatchKey((KeyEvent*)event);
242}
243
244void AInputQueue::finishEvent(AInputEvent* event, bool handled, bool didDefaultHandling) {
245    LOG_TRACE("finishEvent: %p handled=%d, didDefaultHandling=%d", event,
246            handled ? 1 : 0, didDefaultHandling ? 1 : 0);
247
248    if (!handled && !didDefaultHandling
249            && ((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.finishSeq) {
264                status_t res = mConsumer.sendFinishedSignal(inflight.finishSeq, handled);
265                if (res != android::OK) {
266                    ALOGW("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    ALOGW("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    ALOGW("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) ALOGW("Failed writing to dispatch fd: %s", strerror(errno));
414    else ALOGW("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 (looper != NULL && mainWorkRead >= 0) {
444            looper->removeFd(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            } else {
483                return UNKNOWN_ERROR;
484            }
485        }
486        return OK;
487    }
488
489    ANativeActivityCallbacks callbacks;
490
491    void* dlhandle;
492    ANativeActivity_createFunc* createActivityFunc;
493
494    String8 internalDataPathObj;
495    String8 externalDataPathObj;
496    String8 obbPathObj;
497
498    sp<ANativeWindow> nativeWindow;
499    int32_t lastWindowWidth;
500    int32_t lastWindowHeight;
501
502    jobject inputChannel;
503    struct AInputQueue* nativeInputQueue;
504
505    // These are used to wake up the main thread to process work.
506    int mainWorkRead;
507    int mainWorkWrite;
508    sp<Looper> looper;
509};
510
511void android_NativeActivity_finish(ANativeActivity* activity) {
512    NativeCode* code = static_cast<NativeCode*>(activity);
513    write_work(code->mainWorkWrite, CMD_FINISH, 0);
514}
515
516void android_NativeActivity_setWindowFormat(
517        ANativeActivity* activity, int32_t format) {
518    NativeCode* code = static_cast<NativeCode*>(activity);
519    write_work(code->mainWorkWrite, CMD_SET_WINDOW_FORMAT, format);
520}
521
522void android_NativeActivity_setWindowFlags(
523        ANativeActivity* activity, int32_t values, int32_t mask) {
524    NativeCode* code = static_cast<NativeCode*>(activity);
525    write_work(code->mainWorkWrite, CMD_SET_WINDOW_FLAGS, values, mask);
526}
527
528void android_NativeActivity_showSoftInput(
529        ANativeActivity* activity, int32_t flags) {
530    NativeCode* code = static_cast<NativeCode*>(activity);
531    write_work(code->mainWorkWrite, CMD_SHOW_SOFT_INPUT, flags);
532}
533
534void android_NativeActivity_hideSoftInput(
535        ANativeActivity* activity, int32_t flags) {
536    NativeCode* code = static_cast<NativeCode*>(activity);
537    write_work(code->mainWorkWrite, CMD_HIDE_SOFT_INPUT, flags);
538}
539
540// ------------------------------------------------------------------------
541
542static bool checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
543   if (env->ExceptionCheck()) {
544       ALOGE("An exception was thrown by callback '%s'.", methodName);
545       LOGE_EX(env);
546       env->ExceptionClear();
547       return true;
548   }
549   return false;
550}
551
552/*
553 * Callback for handling native events on the application's main thread.
554 */
555static int mainWorkCallback(int fd, int events, void* data) {
556    NativeCode* code = (NativeCode*)data;
557    if ((events & POLLIN) == 0) {
558        return 1;
559    }
560
561    ActivityWork work;
562    if (!read_work(code->mainWorkRead, &work)) {
563        return 1;
564    }
565
566    LOG_TRACE("mainWorkCallback: cmd=%d", work.cmd);
567
568    switch (work.cmd) {
569        case CMD_DEF_KEY: {
570            KeyEvent* keyEvent;
571            while ((keyEvent=code->nativeInputQueue->consumeUnhandledEvent()) != NULL) {
572                jobject inputEventObj = android_view_KeyEvent_fromNative(
573                        code->env, keyEvent);
574                jboolean handled;
575                if (inputEventObj) {
576                    handled = code->env->CallBooleanMethod(code->clazz,
577                            gNativeActivityClassInfo.dispatchUnhandledKeyEvent, inputEventObj);
578                    checkAndClearExceptionFromCallback(code->env, "dispatchUnhandledKeyEvent");
579                    code->env->DeleteLocalRef(inputEventObj);
580                } else {
581                    ALOGE("Failed to obtain key event for dispatchUnhandledKeyEvent.");
582                    handled = false;
583                }
584                code->nativeInputQueue->finishEvent(keyEvent, handled, true);
585            }
586            int seq;
587            while ((keyEvent=code->nativeInputQueue->consumePreDispatchingEvent(&seq)) != NULL) {
588                jobject inputEventObj = android_view_KeyEvent_fromNative(
589                        code->env, keyEvent);
590                if (inputEventObj) {
591                    code->env->CallVoidMethod(code->clazz,
592                            gNativeActivityClassInfo.preDispatchKeyEvent, inputEventObj, seq);
593                    checkAndClearExceptionFromCallback(code->env, "preDispatchKeyEvent");
594                    code->env->DeleteLocalRef(inputEventObj);
595                } else {
596                    ALOGE("Failed to obtain key event for preDispatchKeyEvent.");
597                }
598            }
599        } break;
600        case CMD_FINISH: {
601            code->env->CallVoidMethod(code->clazz, gNativeActivityClassInfo.finish);
602            checkAndClearExceptionFromCallback(code->env, "finish");
603        } break;
604        case CMD_SET_WINDOW_FORMAT: {
605            code->env->CallVoidMethod(code->clazz,
606                    gNativeActivityClassInfo.setWindowFormat, work.arg1);
607            checkAndClearExceptionFromCallback(code->env, "setWindowFormat");
608        } break;
609        case CMD_SET_WINDOW_FLAGS: {
610            code->env->CallVoidMethod(code->clazz,
611                    gNativeActivityClassInfo.setWindowFlags, work.arg1, work.arg2);
612            checkAndClearExceptionFromCallback(code->env, "setWindowFlags");
613        } break;
614        case CMD_SHOW_SOFT_INPUT: {
615            code->env->CallVoidMethod(code->clazz,
616                    gNativeActivityClassInfo.showIme, work.arg1);
617            checkAndClearExceptionFromCallback(code->env, "showIme");
618        } break;
619        case CMD_HIDE_SOFT_INPUT: {
620            code->env->CallVoidMethod(code->clazz,
621                    gNativeActivityClassInfo.hideIme, work.arg1);
622            checkAndClearExceptionFromCallback(code->env, "hideIme");
623        } break;
624        default:
625            ALOGW("Unknown work command: %d", work.cmd);
626            break;
627    }
628
629    return 1;
630}
631
632// ------------------------------------------------------------------------
633
634static jint
635loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path, jstring funcName,
636        jobject messageQueue, jstring internalDataDir, jstring obbDir,
637        jstring externalDataDir, int sdkVersion,
638        jobject jAssetMgr, jbyteArray savedState)
639{
640    LOG_TRACE("loadNativeCode_native");
641
642    const char* pathStr = env->GetStringUTFChars(path, NULL);
643    NativeCode* code = NULL;
644
645    void* handle = dlopen(pathStr, RTLD_LAZY);
646
647    env->ReleaseStringUTFChars(path, pathStr);
648
649    if (handle != NULL) {
650        const char* funcStr = env->GetStringUTFChars(funcName, NULL);
651        code = new NativeCode(handle, (ANativeActivity_createFunc*)
652                dlsym(handle, funcStr));
653        env->ReleaseStringUTFChars(funcName, funcStr);
654
655        if (code->createActivityFunc == NULL) {
656            ALOGW("ANativeActivity_onCreate not found");
657            delete code;
658            return 0;
659        }
660
661        code->looper = android_os_MessageQueue_getLooper(env, messageQueue);
662        if (code->looper == NULL) {
663            ALOGW("Unable to retrieve MessageQueue's Looper");
664            delete code;
665            return 0;
666        }
667
668        int msgpipe[2];
669        if (pipe(msgpipe)) {
670            ALOGW("could not create pipe: %s", strerror(errno));
671            delete code;
672            return 0;
673        }
674        code->mainWorkRead = msgpipe[0];
675        code->mainWorkWrite = msgpipe[1];
676        int result = fcntl(code->mainWorkRead, F_SETFL, O_NONBLOCK);
677        SLOGW_IF(result != 0, "Could not make main work read pipe "
678                "non-blocking: %s", strerror(errno));
679        result = fcntl(code->mainWorkWrite, F_SETFL, O_NONBLOCK);
680        SLOGW_IF(result != 0, "Could not make main work write pipe "
681                "non-blocking: %s", strerror(errno));
682        code->looper->addFd(code->mainWorkRead, 0, ALOOPER_EVENT_INPUT, mainWorkCallback, code);
683
684        code->ANativeActivity::callbacks = &code->callbacks;
685        if (env->GetJavaVM(&code->vm) < 0) {
686            ALOGW("NativeActivity GetJavaVM failed");
687            delete code;
688            return 0;
689        }
690        code->env = env;
691        code->clazz = env->NewGlobalRef(clazz);
692
693        const char* dirStr = env->GetStringUTFChars(internalDataDir, NULL);
694        code->internalDataPathObj = dirStr;
695        code->internalDataPath = code->internalDataPathObj.string();
696        env->ReleaseStringUTFChars(internalDataDir, dirStr);
697
698        dirStr = env->GetStringUTFChars(externalDataDir, NULL);
699        code->externalDataPathObj = dirStr;
700        code->externalDataPath = code->externalDataPathObj.string();
701        env->ReleaseStringUTFChars(externalDataDir, dirStr);
702
703        code->sdkVersion = sdkVersion;
704
705        code->assetManager = assetManagerForJavaObject(env, jAssetMgr);
706
707        dirStr = env->GetStringUTFChars(obbDir, NULL);
708        code->obbPathObj = dirStr;
709        code->obbPath = code->obbPathObj.string();
710        env->ReleaseStringUTFChars(obbDir, dirStr);
711
712        jbyte* rawSavedState = NULL;
713        jsize rawSavedSize = 0;
714        if (savedState != NULL) {
715            rawSavedState = env->GetByteArrayElements(savedState, NULL);
716            rawSavedSize = env->GetArrayLength(savedState);
717        }
718
719        code->createActivityFunc(code, rawSavedState, rawSavedSize);
720
721        if (rawSavedState != NULL) {
722            env->ReleaseByteArrayElements(savedState, rawSavedState, 0);
723        }
724    }
725
726    return (jint)code;
727}
728
729static void
730unloadNativeCode_native(JNIEnv* env, jobject clazz, jint handle)
731{
732    LOG_TRACE("unloadNativeCode_native");
733    if (handle != 0) {
734        NativeCode* code = (NativeCode*)handle;
735        delete code;
736    }
737}
738
739static void
740onStart_native(JNIEnv* env, jobject clazz, jint handle)
741{
742    LOG_TRACE("onStart_native");
743    if (handle != 0) {
744        NativeCode* code = (NativeCode*)handle;
745        if (code->callbacks.onStart != NULL) {
746            code->callbacks.onStart(code);
747        }
748    }
749}
750
751static void
752onResume_native(JNIEnv* env, jobject clazz, jint handle)
753{
754    LOG_TRACE("onResume_native");
755    if (handle != 0) {
756        NativeCode* code = (NativeCode*)handle;
757        if (code->callbacks.onResume != NULL) {
758            code->callbacks.onResume(code);
759        }
760    }
761}
762
763static jbyteArray
764onSaveInstanceState_native(JNIEnv* env, jobject clazz, jint handle)
765{
766    LOG_TRACE("onSaveInstanceState_native");
767
768    jbyteArray array = NULL;
769
770    if (handle != 0) {
771        NativeCode* code = (NativeCode*)handle;
772        if (code->callbacks.onSaveInstanceState != NULL) {
773            size_t len = 0;
774            jbyte* state = (jbyte*)code->callbacks.onSaveInstanceState(code, &len);
775            if (len > 0) {
776                array = env->NewByteArray(len);
777                if (array != NULL) {
778                    env->SetByteArrayRegion(array, 0, len, state);
779                }
780            }
781            if (state != NULL) {
782                free(state);
783            }
784        }
785    }
786
787    return array;
788}
789
790static void
791onPause_native(JNIEnv* env, jobject clazz, jint handle)
792{
793    LOG_TRACE("onPause_native");
794    if (handle != 0) {
795        NativeCode* code = (NativeCode*)handle;
796        if (code->callbacks.onPause != NULL) {
797            code->callbacks.onPause(code);
798        }
799    }
800}
801
802static void
803onStop_native(JNIEnv* env, jobject clazz, jint handle)
804{
805    LOG_TRACE("onStop_native");
806    if (handle != 0) {
807        NativeCode* code = (NativeCode*)handle;
808        if (code->callbacks.onStop != NULL) {
809            code->callbacks.onStop(code);
810        }
811    }
812}
813
814static void
815onConfigurationChanged_native(JNIEnv* env, jobject clazz, jint handle)
816{
817    LOG_TRACE("onConfigurationChanged_native");
818    if (handle != 0) {
819        NativeCode* code = (NativeCode*)handle;
820        if (code->callbacks.onConfigurationChanged != NULL) {
821            code->callbacks.onConfigurationChanged(code);
822        }
823    }
824}
825
826static void
827onLowMemory_native(JNIEnv* env, jobject clazz, jint handle)
828{
829    LOG_TRACE("onLowMemory_native");
830    if (handle != 0) {
831        NativeCode* code = (NativeCode*)handle;
832        if (code->callbacks.onLowMemory != NULL) {
833            code->callbacks.onLowMemory(code);
834        }
835    }
836}
837
838static void
839onWindowFocusChanged_native(JNIEnv* env, jobject clazz, jint handle, jboolean focused)
840{
841    LOG_TRACE("onWindowFocusChanged_native");
842    if (handle != 0) {
843        NativeCode* code = (NativeCode*)handle;
844        if (code->callbacks.onWindowFocusChanged != NULL) {
845            code->callbacks.onWindowFocusChanged(code, focused ? 1 : 0);
846        }
847    }
848}
849
850static void
851onSurfaceCreated_native(JNIEnv* env, jobject clazz, jint handle, jobject surface)
852{
853    LOG_TRACE("onSurfaceCreated_native");
854    if (handle != 0) {
855        NativeCode* code = (NativeCode*)handle;
856        code->setSurface(surface);
857        if (code->nativeWindow != NULL && code->callbacks.onNativeWindowCreated != NULL) {
858            code->callbacks.onNativeWindowCreated(code,
859                    code->nativeWindow.get());
860        }
861    }
862}
863
864static int32_t getWindowProp(ANativeWindow* window, int what) {
865    int value;
866    int res = window->query(window, what, &value);
867    return res < 0 ? res : value;
868}
869
870static void
871onSurfaceChanged_native(JNIEnv* env, jobject clazz, jint handle, jobject surface,
872        jint format, jint width, jint height)
873{
874    LOG_TRACE("onSurfaceChanged_native");
875    if (handle != 0) {
876        NativeCode* code = (NativeCode*)handle;
877        sp<ANativeWindow> oldNativeWindow = code->nativeWindow;
878        code->setSurface(surface);
879        if (oldNativeWindow != code->nativeWindow) {
880            if (oldNativeWindow != NULL && code->callbacks.onNativeWindowDestroyed != NULL) {
881                code->callbacks.onNativeWindowDestroyed(code,
882                        oldNativeWindow.get());
883            }
884            if (code->nativeWindow != NULL) {
885                if (code->callbacks.onNativeWindowCreated != NULL) {
886                    code->callbacks.onNativeWindowCreated(code,
887                            code->nativeWindow.get());
888                }
889                code->lastWindowWidth = getWindowProp(code->nativeWindow.get(),
890                        NATIVE_WINDOW_WIDTH);
891                code->lastWindowHeight = getWindowProp(code->nativeWindow.get(),
892                        NATIVE_WINDOW_HEIGHT);
893            }
894        } else {
895            // Maybe it resized?
896            int32_t newWidth = getWindowProp(code->nativeWindow.get(),
897                    NATIVE_WINDOW_WIDTH);
898            int32_t newHeight = getWindowProp(code->nativeWindow.get(),
899                    NATIVE_WINDOW_HEIGHT);
900            if (newWidth != code->lastWindowWidth
901                    || newHeight != code->lastWindowHeight) {
902                if (code->callbacks.onNativeWindowResized != NULL) {
903                    code->callbacks.onNativeWindowResized(code,
904                            code->nativeWindow.get());
905                }
906            }
907        }
908    }
909}
910
911static void
912onSurfaceRedrawNeeded_native(JNIEnv* env, jobject clazz, jint handle)
913{
914    LOG_TRACE("onSurfaceRedrawNeeded_native");
915    if (handle != 0) {
916        NativeCode* code = (NativeCode*)handle;
917        if (code->nativeWindow != NULL && code->callbacks.onNativeWindowRedrawNeeded != NULL) {
918            code->callbacks.onNativeWindowRedrawNeeded(code, code->nativeWindow.get());
919        }
920    }
921}
922
923static void
924onSurfaceDestroyed_native(JNIEnv* env, jobject clazz, jint handle, jobject surface)
925{
926    LOG_TRACE("onSurfaceDestroyed_native");
927    if (handle != 0) {
928        NativeCode* code = (NativeCode*)handle;
929        if (code->nativeWindow != NULL && code->callbacks.onNativeWindowDestroyed != NULL) {
930            code->callbacks.onNativeWindowDestroyed(code,
931                    code->nativeWindow.get());
932        }
933        code->setSurface(NULL);
934    }
935}
936
937static void
938onInputChannelCreated_native(JNIEnv* env, jobject clazz, jint handle, jobject channel)
939{
940    LOG_TRACE("onInputChannelCreated_native");
941    if (handle != 0) {
942        NativeCode* code = (NativeCode*)handle;
943        status_t err = code->setInputChannel(channel);
944        if (err != OK) {
945            jniThrowException(env, "java/lang/IllegalStateException",
946                    "Error setting input channel");
947            return;
948        }
949        if (code->callbacks.onInputQueueCreated != NULL) {
950            code->callbacks.onInputQueueCreated(code,
951                    code->nativeInputQueue);
952        }
953    }
954}
955
956static void
957onInputChannelDestroyed_native(JNIEnv* env, jobject clazz, jint handle, jobject channel)
958{
959    LOG_TRACE("onInputChannelDestroyed_native");
960    if (handle != 0) {
961        NativeCode* code = (NativeCode*)handle;
962        if (code->nativeInputQueue != NULL
963                && code->callbacks.onInputQueueDestroyed != NULL) {
964            code->callbacks.onInputQueueDestroyed(code,
965                    code->nativeInputQueue);
966        }
967        code->setInputChannel(NULL);
968    }
969}
970
971static void
972onContentRectChanged_native(JNIEnv* env, jobject clazz, jint handle,
973        jint x, jint y, jint w, jint h)
974{
975    LOG_TRACE("onContentRectChanged_native");
976    if (handle != 0) {
977        NativeCode* code = (NativeCode*)handle;
978        if (code->callbacks.onContentRectChanged != NULL) {
979            ARect rect;
980            rect.left = x;
981            rect.top = y;
982            rect.right = x+w;
983            rect.bottom = y+h;
984            code->callbacks.onContentRectChanged(code, &rect);
985        }
986    }
987}
988
989static void
990dispatchKeyEvent_native(JNIEnv* env, jobject clazz, jint handle, jobject eventObj)
991{
992    LOG_TRACE("dispatchKeyEvent_native");
993    if (handle != 0) {
994        NativeCode* code = (NativeCode*)handle;
995        if (code->nativeInputQueue != NULL) {
996            KeyEvent* event = code->nativeInputQueue->createKeyEvent();
997            status_t status = android_view_KeyEvent_toNative(env, eventObj, event);
998            if (status) {
999                delete event;
1000                jniThrowRuntimeException(env, "Could not read contents of KeyEvent object.");
1001                return;
1002            }
1003            code->nativeInputQueue->dispatchEvent(event);
1004        }
1005    }
1006}
1007
1008static void
1009finishPreDispatchKeyEvent_native(JNIEnv* env, jobject clazz, jint handle,
1010        jint seq, jboolean handled)
1011{
1012    LOG_TRACE("finishPreDispatchKeyEvent_native");
1013    if (handle != 0) {
1014        NativeCode* code = (NativeCode*)handle;
1015        if (code->nativeInputQueue != NULL) {
1016            code->nativeInputQueue->finishPreDispatch(seq, handled ? true : false);
1017        }
1018    }
1019}
1020
1021static const JNINativeMethod g_methods[] = {
1022    { "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",
1023            (void*)loadNativeCode_native },
1024    { "unloadNativeCode", "(I)V", (void*)unloadNativeCode_native },
1025    { "onStartNative", "(I)V", (void*)onStart_native },
1026    { "onResumeNative", "(I)V", (void*)onResume_native },
1027    { "onSaveInstanceStateNative", "(I)[B", (void*)onSaveInstanceState_native },
1028    { "onPauseNative", "(I)V", (void*)onPause_native },
1029    { "onStopNative", "(I)V", (void*)onStop_native },
1030    { "onConfigurationChangedNative", "(I)V", (void*)onConfigurationChanged_native },
1031    { "onLowMemoryNative", "(I)V", (void*)onLowMemory_native },
1032    { "onWindowFocusChangedNative", "(IZ)V", (void*)onWindowFocusChanged_native },
1033    { "onSurfaceCreatedNative", "(ILandroid/view/Surface;)V", (void*)onSurfaceCreated_native },
1034    { "onSurfaceChangedNative", "(ILandroid/view/Surface;III)V", (void*)onSurfaceChanged_native },
1035    { "onSurfaceRedrawNeededNative", "(ILandroid/view/Surface;)V", (void*)onSurfaceRedrawNeeded_native },
1036    { "onSurfaceDestroyedNative", "(I)V", (void*)onSurfaceDestroyed_native },
1037    { "onInputChannelCreatedNative", "(ILandroid/view/InputChannel;)V", (void*)onInputChannelCreated_native },
1038    { "onInputChannelDestroyedNative", "(ILandroid/view/InputChannel;)V", (void*)onInputChannelDestroyed_native },
1039    { "onContentRectChangedNative", "(IIIII)V", (void*)onContentRectChanged_native },
1040    { "dispatchKeyEventNative", "(ILandroid/view/KeyEvent;)V", (void*)dispatchKeyEvent_native },
1041    { "finishPreDispatchKeyEventNative", "(IIZ)V", (void*)finishPreDispatchKeyEvent_native },
1042};
1043
1044static const char* const kNativeActivityPathName = "android/app/NativeActivity";
1045
1046#define FIND_CLASS(var, className) \
1047        var = env->FindClass(className); \
1048        LOG_FATAL_IF(! var, "Unable to find class %s", className);
1049
1050#define GET_METHOD_ID(var, clazz, methodName, fieldDescriptor) \
1051        var = env->GetMethodID(clazz, methodName, fieldDescriptor); \
1052        LOG_FATAL_IF(! var, "Unable to find method" methodName);
1053
1054int register_android_app_NativeActivity(JNIEnv* env)
1055{
1056    //ALOGD("register_android_app_NativeActivity");
1057    jclass clazz;
1058    FIND_CLASS(clazz, kNativeActivityPathName);
1059
1060    GET_METHOD_ID(gNativeActivityClassInfo.dispatchUnhandledKeyEvent,
1061            clazz,
1062            "dispatchUnhandledKeyEvent", "(Landroid/view/KeyEvent;)Z");
1063    GET_METHOD_ID(gNativeActivityClassInfo.preDispatchKeyEvent,
1064            clazz,
1065            "preDispatchKeyEvent", "(Landroid/view/KeyEvent;I)V");
1066
1067    GET_METHOD_ID(gNativeActivityClassInfo.finish,
1068            clazz,
1069            "finish", "()V");
1070    GET_METHOD_ID(gNativeActivityClassInfo.setWindowFlags,
1071            clazz,
1072            "setWindowFlags", "(II)V");
1073    GET_METHOD_ID(gNativeActivityClassInfo.setWindowFormat,
1074            clazz,
1075            "setWindowFormat", "(I)V");
1076    GET_METHOD_ID(gNativeActivityClassInfo.showIme,
1077            clazz,
1078            "showIme", "(I)V");
1079    GET_METHOD_ID(gNativeActivityClassInfo.hideIme,
1080            clazz,
1081            "hideIme", "(I)V");
1082
1083    return AndroidRuntime::registerNativeMethods(
1084        env, kNativeActivityPathName,
1085        g_methods, NELEM(g_methods));
1086}
1087
1088} // namespace android
1089