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