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 #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         if (externalDataDir != NULL) {
    310             dirStr = env->GetStringUTFChars(externalDataDir, NULL);
    311             code->externalDataPathObj = dirStr;
    312             env->ReleaseStringUTFChars(externalDataDir, dirStr);
    313         }
    314         code->externalDataPath = code->externalDataPathObj.string();
    315 
    316         code->sdkVersion = sdkVersion;
    317 
    318         code->assetManager = assetManagerForJavaObject(env, jAssetMgr);
    319 
    320         if (obbDir != NULL) {
    321             dirStr = env->GetStringUTFChars(obbDir, NULL);
    322             code->obbPathObj = dirStr;
    323             env->ReleaseStringUTFChars(obbDir, dirStr);
    324         }
    325         code->obbPath = code->obbPathObj.string();
    326 
    327         jbyte* rawSavedState = NULL;
    328         jsize rawSavedSize = 0;
    329         if (savedState != NULL) {
    330             rawSavedState = env->GetByteArrayElements(savedState, NULL);
    331             rawSavedSize = env->GetArrayLength(savedState);
    332         }
    333 
    334         code->createActivityFunc(code, rawSavedState, rawSavedSize);
    335 
    336         if (rawSavedState != NULL) {
    337             env->ReleaseByteArrayElements(savedState, rawSavedState, 0);
    338         }
    339     }
    340 
    341     return (jint)code;
    342 }
    343 
    344 static void
    345 unloadNativeCode_native(JNIEnv* env, jobject clazz, jint handle)
    346 {
    347     LOG_TRACE("unloadNativeCode_native");
    348     if (handle != 0) {
    349         NativeCode* code = (NativeCode*)handle;
    350         delete code;
    351     }
    352 }
    353 
    354 static void
    355 onStart_native(JNIEnv* env, jobject clazz, jint handle)
    356 {
    357     LOG_TRACE("onStart_native");
    358     if (handle != 0) {
    359         NativeCode* code = (NativeCode*)handle;
    360         if (code->callbacks.onStart != NULL) {
    361             code->callbacks.onStart(code);
    362         }
    363     }
    364 }
    365 
    366 static void
    367 onResume_native(JNIEnv* env, jobject clazz, jint handle)
    368 {
    369     LOG_TRACE("onResume_native");
    370     if (handle != 0) {
    371         NativeCode* code = (NativeCode*)handle;
    372         if (code->callbacks.onResume != NULL) {
    373             code->callbacks.onResume(code);
    374         }
    375     }
    376 }
    377 
    378 static jbyteArray
    379 onSaveInstanceState_native(JNIEnv* env, jobject clazz, jint handle)
    380 {
    381     LOG_TRACE("onSaveInstanceState_native");
    382 
    383     jbyteArray array = NULL;
    384 
    385     if (handle != 0) {
    386         NativeCode* code = (NativeCode*)handle;
    387         if (code->callbacks.onSaveInstanceState != NULL) {
    388             size_t len = 0;
    389             jbyte* state = (jbyte*)code->callbacks.onSaveInstanceState(code, &len);
    390             if (len > 0) {
    391                 array = env->NewByteArray(len);
    392                 if (array != NULL) {
    393                     env->SetByteArrayRegion(array, 0, len, state);
    394                 }
    395             }
    396             if (state != NULL) {
    397                 free(state);
    398             }
    399         }
    400     }
    401 
    402     return array;
    403 }
    404 
    405 static void
    406 onPause_native(JNIEnv* env, jobject clazz, jint handle)
    407 {
    408     LOG_TRACE("onPause_native");
    409     if (handle != 0) {
    410         NativeCode* code = (NativeCode*)handle;
    411         if (code->callbacks.onPause != NULL) {
    412             code->callbacks.onPause(code);
    413         }
    414     }
    415 }
    416 
    417 static void
    418 onStop_native(JNIEnv* env, jobject clazz, jint handle)
    419 {
    420     LOG_TRACE("onStop_native");
    421     if (handle != 0) {
    422         NativeCode* code = (NativeCode*)handle;
    423         if (code->callbacks.onStop != NULL) {
    424             code->callbacks.onStop(code);
    425         }
    426     }
    427 }
    428 
    429 static void
    430 onConfigurationChanged_native(JNIEnv* env, jobject clazz, jint handle)
    431 {
    432     LOG_TRACE("onConfigurationChanged_native");
    433     if (handle != 0) {
    434         NativeCode* code = (NativeCode*)handle;
    435         if (code->callbacks.onConfigurationChanged != NULL) {
    436             code->callbacks.onConfigurationChanged(code);
    437         }
    438     }
    439 }
    440 
    441 static void
    442 onLowMemory_native(JNIEnv* env, jobject clazz, jint handle)
    443 {
    444     LOG_TRACE("onLowMemory_native");
    445     if (handle != 0) {
    446         NativeCode* code = (NativeCode*)handle;
    447         if (code->callbacks.onLowMemory != NULL) {
    448             code->callbacks.onLowMemory(code);
    449         }
    450     }
    451 }
    452 
    453 static void
    454 onWindowFocusChanged_native(JNIEnv* env, jobject clazz, jint handle, jboolean focused)
    455 {
    456     LOG_TRACE("onWindowFocusChanged_native");
    457     if (handle != 0) {
    458         NativeCode* code = (NativeCode*)handle;
    459         if (code->callbacks.onWindowFocusChanged != NULL) {
    460             code->callbacks.onWindowFocusChanged(code, focused ? 1 : 0);
    461         }
    462     }
    463 }
    464 
    465 static void
    466 onSurfaceCreated_native(JNIEnv* env, jobject clazz, jint handle, jobject surface)
    467 {
    468     LOG_TRACE("onSurfaceCreated_native");
    469     if (handle != 0) {
    470         NativeCode* code = (NativeCode*)handle;
    471         code->setSurface(surface);
    472         if (code->nativeWindow != NULL && code->callbacks.onNativeWindowCreated != NULL) {
    473             code->callbacks.onNativeWindowCreated(code,
    474                     code->nativeWindow.get());
    475         }
    476     }
    477 }
    478 
    479 static int32_t getWindowProp(ANativeWindow* window, int what) {
    480     int value;
    481     int res = window->query(window, what, &value);
    482     return res < 0 ? res : value;
    483 }
    484 
    485 static void
    486 onSurfaceChanged_native(JNIEnv* env, jobject clazz, jint handle, jobject surface,
    487         jint format, jint width, jint height)
    488 {
    489     LOG_TRACE("onSurfaceChanged_native");
    490     if (handle != 0) {
    491         NativeCode* code = (NativeCode*)handle;
    492         sp<ANativeWindow> oldNativeWindow = code->nativeWindow;
    493         code->setSurface(surface);
    494         if (oldNativeWindow != code->nativeWindow) {
    495             if (oldNativeWindow != NULL && code->callbacks.onNativeWindowDestroyed != NULL) {
    496                 code->callbacks.onNativeWindowDestroyed(code,
    497                         oldNativeWindow.get());
    498             }
    499             if (code->nativeWindow != NULL) {
    500                 if (code->callbacks.onNativeWindowCreated != NULL) {
    501                     code->callbacks.onNativeWindowCreated(code,
    502                             code->nativeWindow.get());
    503                 }
    504                 code->lastWindowWidth = getWindowProp(code->nativeWindow.get(),
    505                         NATIVE_WINDOW_WIDTH);
    506                 code->lastWindowHeight = getWindowProp(code->nativeWindow.get(),
    507                         NATIVE_WINDOW_HEIGHT);
    508             }
    509         } else {
    510             // Maybe it resized?
    511             int32_t newWidth = getWindowProp(code->nativeWindow.get(),
    512                     NATIVE_WINDOW_WIDTH);
    513             int32_t newHeight = getWindowProp(code->nativeWindow.get(),
    514                     NATIVE_WINDOW_HEIGHT);
    515             if (newWidth != code->lastWindowWidth
    516                     || newHeight != code->lastWindowHeight) {
    517                 if (code->callbacks.onNativeWindowResized != NULL) {
    518                     code->callbacks.onNativeWindowResized(code,
    519                             code->nativeWindow.get());
    520                 }
    521             }
    522         }
    523     }
    524 }
    525 
    526 static void
    527 onSurfaceRedrawNeeded_native(JNIEnv* env, jobject clazz, jint handle)
    528 {
    529     LOG_TRACE("onSurfaceRedrawNeeded_native");
    530     if (handle != 0) {
    531         NativeCode* code = (NativeCode*)handle;
    532         if (code->nativeWindow != NULL && code->callbacks.onNativeWindowRedrawNeeded != NULL) {
    533             code->callbacks.onNativeWindowRedrawNeeded(code, code->nativeWindow.get());
    534         }
    535     }
    536 }
    537 
    538 static void
    539 onSurfaceDestroyed_native(JNIEnv* env, jobject clazz, jint handle, jobject surface)
    540 {
    541     LOG_TRACE("onSurfaceDestroyed_native");
    542     if (handle != 0) {
    543         NativeCode* code = (NativeCode*)handle;
    544         if (code->nativeWindow != NULL && code->callbacks.onNativeWindowDestroyed != NULL) {
    545             code->callbacks.onNativeWindowDestroyed(code,
    546                     code->nativeWindow.get());
    547         }
    548         code->setSurface(NULL);
    549     }
    550 }
    551 
    552 static void
    553 onInputQueueCreated_native(JNIEnv* env, jobject clazz, jint handle, jint queuePtr)
    554 {
    555     LOG_TRACE("onInputChannelCreated_native");
    556     if (handle != 0) {
    557         NativeCode* code = (NativeCode*)handle;
    558         if (code->callbacks.onInputQueueCreated != NULL) {
    559             AInputQueue* queue = reinterpret_cast<AInputQueue*>(queuePtr);
    560             code->callbacks.onInputQueueCreated(code, queue);
    561         }
    562     }
    563 }
    564 
    565 static void
    566 onInputQueueDestroyed_native(JNIEnv* env, jobject clazz, jint handle, jint queuePtr)
    567 {
    568     LOG_TRACE("onInputChannelDestroyed_native");
    569     if (handle != 0) {
    570         NativeCode* code = (NativeCode*)handle;
    571         if (code->callbacks.onInputQueueDestroyed != NULL) {
    572             AInputQueue* queue = reinterpret_cast<AInputQueue*>(queuePtr);
    573             code->callbacks.onInputQueueDestroyed(code, queue);
    574         }
    575     }
    576 }
    577 
    578 static void
    579 onContentRectChanged_native(JNIEnv* env, jobject clazz, jint handle,
    580         jint x, jint y, jint w, jint h)
    581 {
    582     LOG_TRACE("onContentRectChanged_native");
    583     if (handle != 0) {
    584         NativeCode* code = (NativeCode*)handle;
    585         if (code->callbacks.onContentRectChanged != NULL) {
    586             ARect rect;
    587             rect.left = x;
    588             rect.top = y;
    589             rect.right = x+w;
    590             rect.bottom = y+h;
    591             code->callbacks.onContentRectChanged(code, &rect);
    592         }
    593     }
    594 }
    595 
    596 static const JNINativeMethod g_methods[] = {
    597     { "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",
    598             (void*)loadNativeCode_native },
    599     { "unloadNativeCode", "(I)V", (void*)unloadNativeCode_native },
    600     { "onStartNative", "(I)V", (void*)onStart_native },
    601     { "onResumeNative", "(I)V", (void*)onResume_native },
    602     { "onSaveInstanceStateNative", "(I)[B", (void*)onSaveInstanceState_native },
    603     { "onPauseNative", "(I)V", (void*)onPause_native },
    604     { "onStopNative", "(I)V", (void*)onStop_native },
    605     { "onConfigurationChangedNative", "(I)V", (void*)onConfigurationChanged_native },
    606     { "onLowMemoryNative", "(I)V", (void*)onLowMemory_native },
    607     { "onWindowFocusChangedNative", "(IZ)V", (void*)onWindowFocusChanged_native },
    608     { "onSurfaceCreatedNative", "(ILandroid/view/Surface;)V", (void*)onSurfaceCreated_native },
    609     { "onSurfaceChangedNative", "(ILandroid/view/Surface;III)V", (void*)onSurfaceChanged_native },
    610     { "onSurfaceRedrawNeededNative", "(ILandroid/view/Surface;)V", (void*)onSurfaceRedrawNeeded_native },
    611     { "onSurfaceDestroyedNative", "(I)V", (void*)onSurfaceDestroyed_native },
    612     { "onInputQueueCreatedNative", "(II)V",
    613         (void*)onInputQueueCreated_native },
    614     { "onInputQueueDestroyedNative", "(II)V",
    615         (void*)onInputQueueDestroyed_native },
    616     { "onContentRectChangedNative", "(IIIII)V", (void*)onContentRectChanged_native },
    617 };
    618 
    619 static const char* const kNativeActivityPathName = "android/app/NativeActivity";
    620 
    621 #define FIND_CLASS(var, className) \
    622         var = env->FindClass(className); \
    623         LOG_FATAL_IF(! var, "Unable to find class %s", className);
    624 
    625 #define GET_METHOD_ID(var, clazz, methodName, fieldDescriptor) \
    626         var = env->GetMethodID(clazz, methodName, fieldDescriptor); \
    627         LOG_FATAL_IF(! var, "Unable to find method" methodName);
    628 
    629 int register_android_app_NativeActivity(JNIEnv* env)
    630 {
    631     //ALOGD("register_android_app_NativeActivity");
    632     jclass clazz;
    633     FIND_CLASS(clazz, kNativeActivityPathName);
    634 
    635     GET_METHOD_ID(gNativeActivityClassInfo.finish,
    636             clazz,
    637             "finish", "()V");
    638     GET_METHOD_ID(gNativeActivityClassInfo.setWindowFlags,
    639             clazz,
    640             "setWindowFlags", "(II)V");
    641     GET_METHOD_ID(gNativeActivityClassInfo.setWindowFormat,
    642             clazz,
    643             "setWindowFormat", "(I)V");
    644     GET_METHOD_ID(gNativeActivityClassInfo.showIme,
    645             clazz,
    646             "showIme", "(I)V");
    647     GET_METHOD_ID(gNativeActivityClassInfo.hideIme,
    648             clazz,
    649             "hideIme", "(I)V");
    650 
    651     return AndroidRuntime::registerNativeMethods(
    652         env, kNativeActivityPathName,
    653         g_methods, NELEM(g_methods));
    654 }
    655 
    656 } // namespace android
    657