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