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/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 <input/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 #include "nativebridge/native_bridge.h"
     42 
     43 #define LOG_TRACE(...)
     44 //#define LOG_TRACE(...) ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)
     45 
     46 namespace android
     47 {
     48 
     49 static struct {
     50     jmethodID finish;
     51     jmethodID setWindowFlags;
     52     jmethodID setWindowFormat;
     53     jmethodID showIme;
     54     jmethodID hideIme;
     55 } gNativeActivityClassInfo;
     56 
     57 // ------------------------------------------------------------------------
     58 
     59 struct ActivityWork {
     60     int32_t cmd;
     61     int32_t arg1;
     62     int32_t arg2;
     63 };
     64 
     65 enum {
     66     CMD_FINISH = 1,
     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) ALOGW("Failed writing to work fd: %s", strerror(errno));
     90     else ALOGW("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) ALOGW("Failed reading work fd: %s", strerror(errno));
     99     else ALOGW("Truncated reading work fd: %d", res);
    100     return false;
    101 }
    102 
    103 /*
    104  * Native state for interacting with the NativeActivity class.
    105  */
    106 struct NativeCode : public ANativeActivity {
    107     NativeCode(void* _dlhandle, ANativeActivity_createFunc* _createFunc) {
    108         memset((ANativeActivity*)this, 0, sizeof(ANativeActivity));
    109         memset(&callbacks, 0, sizeof(callbacks));
    110         dlhandle = _dlhandle;
    111         createActivityFunc = _createFunc;
    112         nativeWindow = NULL;
    113         mainWorkRead = mainWorkWrite = -1;
    114     }
    115 
    116     ~NativeCode() {
    117         if (callbacks.onDestroy != NULL) {
    118             callbacks.onDestroy(this);
    119         }
    120         if (env != NULL && clazz != NULL) {
    121             env->DeleteGlobalRef(clazz);
    122         }
    123         if (messageQueue != NULL && mainWorkRead >= 0) {
    124             messageQueue->getLooper()->removeFd(mainWorkRead);
    125         }
    126         setSurface(NULL);
    127         if (mainWorkRead >= 0) close(mainWorkRead);
    128         if (mainWorkWrite >= 0) close(mainWorkWrite);
    129         if (dlhandle != NULL) {
    130             // for now don't unload...  we probably should clean this
    131             // up and only keep one open dlhandle per proc, since there
    132             // is really no benefit to unloading the code.
    133             //dlclose(dlhandle);
    134         }
    135     }
    136 
    137     void setSurface(jobject _surface) {
    138         if (_surface != NULL) {
    139             nativeWindow = android_view_Surface_getNativeWindow(env, _surface);
    140         } else {
    141             nativeWindow = NULL;
    142         }
    143     }
    144 
    145     ANativeActivityCallbacks callbacks;
    146 
    147     void* dlhandle;
    148     ANativeActivity_createFunc* createActivityFunc;
    149 
    150     String8 internalDataPathObj;
    151     String8 externalDataPathObj;
    152     String8 obbPathObj;
    153 
    154     sp<ANativeWindow> nativeWindow;
    155     int32_t lastWindowWidth;
    156     int32_t lastWindowHeight;
    157 
    158     // These are used to wake up the main thread to process work.
    159     int mainWorkRead;
    160     int mainWorkWrite;
    161     sp<MessageQueue> messageQueue;
    162 };
    163 
    164 void android_NativeActivity_finish(ANativeActivity* activity) {
    165     NativeCode* code = static_cast<NativeCode*>(activity);
    166     write_work(code->mainWorkWrite, CMD_FINISH, 0);
    167 }
    168 
    169 void android_NativeActivity_setWindowFormat(
    170         ANativeActivity* activity, int32_t format) {
    171     NativeCode* code = static_cast<NativeCode*>(activity);
    172     write_work(code->mainWorkWrite, CMD_SET_WINDOW_FORMAT, format);
    173 }
    174 
    175 void android_NativeActivity_setWindowFlags(
    176         ANativeActivity* activity, int32_t values, int32_t mask) {
    177     NativeCode* code = static_cast<NativeCode*>(activity);
    178     write_work(code->mainWorkWrite, CMD_SET_WINDOW_FLAGS, values, mask);
    179 }
    180 
    181 void android_NativeActivity_showSoftInput(
    182         ANativeActivity* activity, int32_t flags) {
    183     NativeCode* code = static_cast<NativeCode*>(activity);
    184     write_work(code->mainWorkWrite, CMD_SHOW_SOFT_INPUT, flags);
    185 }
    186 
    187 void android_NativeActivity_hideSoftInput(
    188         ANativeActivity* activity, int32_t flags) {
    189     NativeCode* code = static_cast<NativeCode*>(activity);
    190     write_work(code->mainWorkWrite, CMD_HIDE_SOFT_INPUT, flags);
    191 }
    192 
    193 // ------------------------------------------------------------------------
    194 
    195 /*
    196  * Callback for handling native events on the application's main thread.
    197  */
    198 static int mainWorkCallback(int fd, int events, void* data) {
    199     NativeCode* code = (NativeCode*)data;
    200     if ((events & POLLIN) == 0) {
    201         return 1;
    202     }
    203 
    204     ActivityWork work;
    205     if (!read_work(code->mainWorkRead, &work)) {
    206         return 1;
    207     }
    208 
    209     LOG_TRACE("mainWorkCallback: cmd=%d", work.cmd);
    210 
    211     switch (work.cmd) {
    212         case CMD_FINISH: {
    213             code->env->CallVoidMethod(code->clazz, gNativeActivityClassInfo.finish);
    214             code->messageQueue->raiseAndClearException(code->env, "finish");
    215         } break;
    216         case CMD_SET_WINDOW_FORMAT: {
    217             code->env->CallVoidMethod(code->clazz,
    218                     gNativeActivityClassInfo.setWindowFormat, work.arg1);
    219             code->messageQueue->raiseAndClearException(code->env, "setWindowFormat");
    220         } break;
    221         case CMD_SET_WINDOW_FLAGS: {
    222             code->env->CallVoidMethod(code->clazz,
    223                     gNativeActivityClassInfo.setWindowFlags, work.arg1, work.arg2);
    224             code->messageQueue->raiseAndClearException(code->env, "setWindowFlags");
    225         } break;
    226         case CMD_SHOW_SOFT_INPUT: {
    227             code->env->CallVoidMethod(code->clazz,
    228                     gNativeActivityClassInfo.showIme, work.arg1);
    229             code->messageQueue->raiseAndClearException(code->env, "showIme");
    230         } break;
    231         case CMD_HIDE_SOFT_INPUT: {
    232             code->env->CallVoidMethod(code->clazz,
    233                     gNativeActivityClassInfo.hideIme, work.arg1);
    234             code->messageQueue->raiseAndClearException(code->env, "hideIme");
    235         } break;
    236         default:
    237             ALOGW("Unknown work command: %d", work.cmd);
    238             break;
    239     }
    240 
    241     return 1;
    242 }
    243 
    244 // ------------------------------------------------------------------------
    245 
    246 static jlong
    247 loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path, jstring funcName,
    248         jobject messageQueue, jstring internalDataDir, jstring obbDir,
    249         jstring externalDataDir, jint sdkVersion,
    250         jobject jAssetMgr, jbyteArray savedState)
    251 {
    252     LOG_TRACE("loadNativeCode_native");
    253 
    254     const char* pathStr = env->GetStringUTFChars(path, NULL);
    255     NativeCode* code = NULL;
    256     bool needNativeBridge = false;
    257 
    258     void* handle = dlopen(pathStr, RTLD_LAZY);
    259     if (handle == NULL) {
    260         if (NativeBridgeIsSupported(pathStr)) {
    261             handle = NativeBridgeLoadLibrary(pathStr, RTLD_LAZY);
    262             needNativeBridge = true;
    263         }
    264     }
    265     env->ReleaseStringUTFChars(path, pathStr);
    266 
    267     if (handle != NULL) {
    268         void* funcPtr = NULL;
    269         const char* funcStr = env->GetStringUTFChars(funcName, NULL);
    270         if (needNativeBridge) {
    271             funcPtr = NativeBridgeGetTrampoline(handle, funcStr, NULL, 0);
    272         } else {
    273             funcPtr = dlsym(handle, funcStr);
    274         }
    275 
    276         code = new NativeCode(handle, (ANativeActivity_createFunc*)funcPtr);
    277         env->ReleaseStringUTFChars(funcName, funcStr);
    278 
    279         if (code->createActivityFunc == NULL) {
    280             ALOGW("ANativeActivity_onCreate not found");
    281             delete code;
    282             return 0;
    283         }
    284 
    285         code->messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueue);
    286         if (code->messageQueue == NULL) {
    287             ALOGW("Unable to retrieve native MessageQueue");
    288             delete code;
    289             return 0;
    290         }
    291 
    292         int msgpipe[2];
    293         if (pipe(msgpipe)) {
    294             ALOGW("could not create pipe: %s", strerror(errno));
    295             delete code;
    296             return 0;
    297         }
    298         code->mainWorkRead = msgpipe[0];
    299         code->mainWorkWrite = msgpipe[1];
    300         int result = fcntl(code->mainWorkRead, F_SETFL, O_NONBLOCK);
    301         SLOGW_IF(result != 0, "Could not make main work read pipe "
    302                 "non-blocking: %s", strerror(errno));
    303         result = fcntl(code->mainWorkWrite, F_SETFL, O_NONBLOCK);
    304         SLOGW_IF(result != 0, "Could not make main work write pipe "
    305                 "non-blocking: %s", strerror(errno));
    306         code->messageQueue->getLooper()->addFd(
    307                 code->mainWorkRead, 0, ALOOPER_EVENT_INPUT, mainWorkCallback, code);
    308 
    309         code->ANativeActivity::callbacks = &code->callbacks;
    310         if (env->GetJavaVM(&code->vm) < 0) {
    311             ALOGW("NativeActivity GetJavaVM failed");
    312             delete code;
    313             return 0;
    314         }
    315         code->env = env;
    316         code->clazz = env->NewGlobalRef(clazz);
    317 
    318         const char* dirStr = env->GetStringUTFChars(internalDataDir, NULL);
    319         code->internalDataPathObj = dirStr;
    320         code->internalDataPath = code->internalDataPathObj.string();
    321         env->ReleaseStringUTFChars(internalDataDir, dirStr);
    322 
    323         if (externalDataDir != NULL) {
    324             dirStr = env->GetStringUTFChars(externalDataDir, NULL);
    325             code->externalDataPathObj = dirStr;
    326             env->ReleaseStringUTFChars(externalDataDir, dirStr);
    327         }
    328         code->externalDataPath = code->externalDataPathObj.string();
    329 
    330         code->sdkVersion = sdkVersion;
    331 
    332         code->assetManager = assetManagerForJavaObject(env, jAssetMgr);
    333 
    334         if (obbDir != NULL) {
    335             dirStr = env->GetStringUTFChars(obbDir, NULL);
    336             code->obbPathObj = dirStr;
    337             env->ReleaseStringUTFChars(obbDir, dirStr);
    338         }
    339         code->obbPath = code->obbPathObj.string();
    340 
    341         jbyte* rawSavedState = NULL;
    342         jsize rawSavedSize = 0;
    343         if (savedState != NULL) {
    344             rawSavedState = env->GetByteArrayElements(savedState, NULL);
    345             rawSavedSize = env->GetArrayLength(savedState);
    346         }
    347 
    348         code->createActivityFunc(code, rawSavedState, rawSavedSize);
    349 
    350         if (rawSavedState != NULL) {
    351             env->ReleaseByteArrayElements(savedState, rawSavedState, 0);
    352         }
    353     }
    354 
    355     return (jlong)code;
    356 }
    357 
    358 static void
    359 unloadNativeCode_native(JNIEnv* env, jobject clazz, jlong handle)
    360 {
    361     LOG_TRACE("unloadNativeCode_native");
    362     if (handle != 0) {
    363         NativeCode* code = (NativeCode*)handle;
    364         delete code;
    365     }
    366 }
    367 
    368 static void
    369 onStart_native(JNIEnv* env, jobject clazz, jlong handle)
    370 {
    371     LOG_TRACE("onStart_native");
    372     if (handle != 0) {
    373         NativeCode* code = (NativeCode*)handle;
    374         if (code->callbacks.onStart != NULL) {
    375             code->callbacks.onStart(code);
    376         }
    377     }
    378 }
    379 
    380 static void
    381 onResume_native(JNIEnv* env, jobject clazz, jlong handle)
    382 {
    383     LOG_TRACE("onResume_native");
    384     if (handle != 0) {
    385         NativeCode* code = (NativeCode*)handle;
    386         if (code->callbacks.onResume != NULL) {
    387             code->callbacks.onResume(code);
    388         }
    389     }
    390 }
    391 
    392 static jbyteArray
    393 onSaveInstanceState_native(JNIEnv* env, jobject clazz, jlong handle)
    394 {
    395     LOG_TRACE("onSaveInstanceState_native");
    396 
    397     jbyteArray array = NULL;
    398 
    399     if (handle != 0) {
    400         NativeCode* code = (NativeCode*)handle;
    401         if (code->callbacks.onSaveInstanceState != NULL) {
    402             size_t len = 0;
    403             jbyte* state = (jbyte*)code->callbacks.onSaveInstanceState(code, &len);
    404             if (len > 0) {
    405                 array = env->NewByteArray(len);
    406                 if (array != NULL) {
    407                     env->SetByteArrayRegion(array, 0, len, state);
    408                 }
    409             }
    410             if (state != NULL) {
    411                 free(state);
    412             }
    413         }
    414     }
    415 
    416     return array;
    417 }
    418 
    419 static void
    420 onPause_native(JNIEnv* env, jobject clazz, jlong handle)
    421 {
    422     LOG_TRACE("onPause_native");
    423     if (handle != 0) {
    424         NativeCode* code = (NativeCode*)handle;
    425         if (code->callbacks.onPause != NULL) {
    426             code->callbacks.onPause(code);
    427         }
    428     }
    429 }
    430 
    431 static void
    432 onStop_native(JNIEnv* env, jobject clazz, jlong handle)
    433 {
    434     LOG_TRACE("onStop_native");
    435     if (handle != 0) {
    436         NativeCode* code = (NativeCode*)handle;
    437         if (code->callbacks.onStop != NULL) {
    438             code->callbacks.onStop(code);
    439         }
    440     }
    441 }
    442 
    443 static void
    444 onConfigurationChanged_native(JNIEnv* env, jobject clazz, jlong handle)
    445 {
    446     LOG_TRACE("onConfigurationChanged_native");
    447     if (handle != 0) {
    448         NativeCode* code = (NativeCode*)handle;
    449         if (code->callbacks.onConfigurationChanged != NULL) {
    450             code->callbacks.onConfigurationChanged(code);
    451         }
    452     }
    453 }
    454 
    455 static void
    456 onLowMemory_native(JNIEnv* env, jobject clazz, jlong handle)
    457 {
    458     LOG_TRACE("onLowMemory_native");
    459     if (handle != 0) {
    460         NativeCode* code = (NativeCode*)handle;
    461         if (code->callbacks.onLowMemory != NULL) {
    462             code->callbacks.onLowMemory(code);
    463         }
    464     }
    465 }
    466 
    467 static void
    468 onWindowFocusChanged_native(JNIEnv* env, jobject clazz, jlong handle, jboolean focused)
    469 {
    470     LOG_TRACE("onWindowFocusChanged_native");
    471     if (handle != 0) {
    472         NativeCode* code = (NativeCode*)handle;
    473         if (code->callbacks.onWindowFocusChanged != NULL) {
    474             code->callbacks.onWindowFocusChanged(code, focused ? 1 : 0);
    475         }
    476     }
    477 }
    478 
    479 static void
    480 onSurfaceCreated_native(JNIEnv* env, jobject clazz, jlong handle, jobject surface)
    481 {
    482     LOG_TRACE("onSurfaceCreated_native");
    483     if (handle != 0) {
    484         NativeCode* code = (NativeCode*)handle;
    485         code->setSurface(surface);
    486         if (code->nativeWindow != NULL && code->callbacks.onNativeWindowCreated != NULL) {
    487             code->callbacks.onNativeWindowCreated(code,
    488                     code->nativeWindow.get());
    489         }
    490     }
    491 }
    492 
    493 static int32_t getWindowProp(ANativeWindow* window, int what) {
    494     int value;
    495     int res = window->query(window, what, &value);
    496     return res < 0 ? res : value;
    497 }
    498 
    499 static void
    500 onSurfaceChanged_native(JNIEnv* env, jobject clazz, jlong handle, jobject surface,
    501         jint format, jint width, jint height)
    502 {
    503     LOG_TRACE("onSurfaceChanged_native");
    504     if (handle != 0) {
    505         NativeCode* code = (NativeCode*)handle;
    506         sp<ANativeWindow> oldNativeWindow = code->nativeWindow;
    507         code->setSurface(surface);
    508         if (oldNativeWindow != code->nativeWindow) {
    509             if (oldNativeWindow != NULL && code->callbacks.onNativeWindowDestroyed != NULL) {
    510                 code->callbacks.onNativeWindowDestroyed(code,
    511                         oldNativeWindow.get());
    512             }
    513             if (code->nativeWindow != NULL) {
    514                 if (code->callbacks.onNativeWindowCreated != NULL) {
    515                     code->callbacks.onNativeWindowCreated(code,
    516                             code->nativeWindow.get());
    517                 }
    518                 code->lastWindowWidth = getWindowProp(code->nativeWindow.get(),
    519                         NATIVE_WINDOW_WIDTH);
    520                 code->lastWindowHeight = getWindowProp(code->nativeWindow.get(),
    521                         NATIVE_WINDOW_HEIGHT);
    522             }
    523         } else {
    524             // Maybe it resized?
    525             int32_t newWidth = getWindowProp(code->nativeWindow.get(),
    526                     NATIVE_WINDOW_WIDTH);
    527             int32_t newHeight = getWindowProp(code->nativeWindow.get(),
    528                     NATIVE_WINDOW_HEIGHT);
    529             if (newWidth != code->lastWindowWidth
    530                     || newHeight != code->lastWindowHeight) {
    531                 if (code->callbacks.onNativeWindowResized != NULL) {
    532                     code->callbacks.onNativeWindowResized(code,
    533                             code->nativeWindow.get());
    534                 }
    535             }
    536         }
    537     }
    538 }
    539 
    540 static void
    541 onSurfaceRedrawNeeded_native(JNIEnv* env, jobject clazz, jlong handle)
    542 {
    543     LOG_TRACE("onSurfaceRedrawNeeded_native");
    544     if (handle != 0) {
    545         NativeCode* code = (NativeCode*)handle;
    546         if (code->nativeWindow != NULL && code->callbacks.onNativeWindowRedrawNeeded != NULL) {
    547             code->callbacks.onNativeWindowRedrawNeeded(code, code->nativeWindow.get());
    548         }
    549     }
    550 }
    551 
    552 static void
    553 onSurfaceDestroyed_native(JNIEnv* env, jobject clazz, jlong handle, jobject surface)
    554 {
    555     LOG_TRACE("onSurfaceDestroyed_native");
    556     if (handle != 0) {
    557         NativeCode* code = (NativeCode*)handle;
    558         if (code->nativeWindow != NULL && code->callbacks.onNativeWindowDestroyed != NULL) {
    559             code->callbacks.onNativeWindowDestroyed(code,
    560                     code->nativeWindow.get());
    561         }
    562         code->setSurface(NULL);
    563     }
    564 }
    565 
    566 static void
    567 onInputQueueCreated_native(JNIEnv* env, jobject clazz, jlong handle, jlong queuePtr)
    568 {
    569     LOG_TRACE("onInputChannelCreated_native");
    570     if (handle != 0) {
    571         NativeCode* code = (NativeCode*)handle;
    572         if (code->callbacks.onInputQueueCreated != NULL) {
    573             AInputQueue* queue = reinterpret_cast<AInputQueue*>(queuePtr);
    574             code->callbacks.onInputQueueCreated(code, queue);
    575         }
    576     }
    577 }
    578 
    579 static void
    580 onInputQueueDestroyed_native(JNIEnv* env, jobject clazz, jlong handle, jlong queuePtr)
    581 {
    582     LOG_TRACE("onInputChannelDestroyed_native");
    583     if (handle != 0) {
    584         NativeCode* code = (NativeCode*)handle;
    585         if (code->callbacks.onInputQueueDestroyed != NULL) {
    586             AInputQueue* queue = reinterpret_cast<AInputQueue*>(queuePtr);
    587             code->callbacks.onInputQueueDestroyed(code, queue);
    588         }
    589     }
    590 }
    591 
    592 static void
    593 onContentRectChanged_native(JNIEnv* env, jobject clazz, jlong handle,
    594         jint x, jint y, jint w, jint h)
    595 {
    596     LOG_TRACE("onContentRectChanged_native");
    597     if (handle != 0) {
    598         NativeCode* code = (NativeCode*)handle;
    599         if (code->callbacks.onContentRectChanged != NULL) {
    600             ARect rect;
    601             rect.left = x;
    602             rect.top = y;
    603             rect.right = x+w;
    604             rect.bottom = y+h;
    605             code->callbacks.onContentRectChanged(code, &rect);
    606         }
    607     }
    608 }
    609 
    610 static const JNINativeMethod g_methods[] = {
    611     { "loadNativeCode", "(Ljava/lang/String;Ljava/lang/String;Landroid/os/MessageQueue;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILandroid/content/res/AssetManager;[B)J",
    612             (void*)loadNativeCode_native },
    613     { "unloadNativeCode", "(J)V", (void*)unloadNativeCode_native },
    614     { "onStartNative", "(J)V", (void*)onStart_native },
    615     { "onResumeNative", "(J)V", (void*)onResume_native },
    616     { "onSaveInstanceStateNative", "(J)[B", (void*)onSaveInstanceState_native },
    617     { "onPauseNative", "(J)V", (void*)onPause_native },
    618     { "onStopNative", "(J)V", (void*)onStop_native },
    619     { "onConfigurationChangedNative", "(J)V", (void*)onConfigurationChanged_native },
    620     { "onLowMemoryNative", "(J)V", (void*)onLowMemory_native },
    621     { "onWindowFocusChangedNative", "(JZ)V", (void*)onWindowFocusChanged_native },
    622     { "onSurfaceCreatedNative", "(JLandroid/view/Surface;)V", (void*)onSurfaceCreated_native },
    623     { "onSurfaceChangedNative", "(JLandroid/view/Surface;III)V", (void*)onSurfaceChanged_native },
    624     { "onSurfaceRedrawNeededNative", "(JLandroid/view/Surface;)V", (void*)onSurfaceRedrawNeeded_native },
    625     { "onSurfaceDestroyedNative", "(J)V", (void*)onSurfaceDestroyed_native },
    626     { "onInputQueueCreatedNative", "(JJ)V",
    627         (void*)onInputQueueCreated_native },
    628     { "onInputQueueDestroyedNative", "(JJ)V",
    629         (void*)onInputQueueDestroyed_native },
    630     { "onContentRectChangedNative", "(JIIII)V", (void*)onContentRectChanged_native },
    631 };
    632 
    633 static const char* const kNativeActivityPathName = "android/app/NativeActivity";
    634 
    635 #define FIND_CLASS(var, className) \
    636         var = env->FindClass(className); \
    637         LOG_FATAL_IF(! var, "Unable to find class %s", className);
    638 
    639 #define GET_METHOD_ID(var, clazz, methodName, fieldDescriptor) \
    640         var = env->GetMethodID(clazz, methodName, fieldDescriptor); \
    641         LOG_FATAL_IF(! var, "Unable to find method" methodName);
    642 
    643 int register_android_app_NativeActivity(JNIEnv* env)
    644 {
    645     //ALOGD("register_android_app_NativeActivity");
    646     jclass clazz;
    647     FIND_CLASS(clazz, kNativeActivityPathName);
    648 
    649     GET_METHOD_ID(gNativeActivityClassInfo.finish,
    650             clazz,
    651             "finish", "()V");
    652     GET_METHOD_ID(gNativeActivityClassInfo.setWindowFlags,
    653             clazz,
    654             "setWindowFlags", "(II)V");
    655     GET_METHOD_ID(gNativeActivityClassInfo.setWindowFormat,
    656             clazz,
    657             "setWindowFormat", "(I)V");
    658     GET_METHOD_ID(gNativeActivityClassInfo.showIme,
    659             clazz,
    660             "showIme", "(I)V");
    661     GET_METHOD_ID(gNativeActivityClassInfo.hideIme,
    662             clazz,
    663             "hideIme", "(I)V");
    664 
    665     return AndroidRuntime::registerNativeMethods(
    666         env, kNativeActivityPathName,
    667         g_methods, NELEM(g_methods));
    668 }
    669 
    670 } // namespace android
    671