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