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