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