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