Home | History | Annotate | Download | only in jni
      1 /*
      2  * Copyright (C) 2008 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_NDEBUG 0
     18 #define LOG_TAG "JET_JNI"
     19 
     20 
     21 #include <stdio.h>
     22 #include <unistd.h>
     23 #include <fcntl.h>
     24 
     25 #include "jni.h"
     26 #include "JNIHelp.h"
     27 #include "android_runtime/AndroidRuntime.h"
     28 
     29 #include "utils/Log.h"
     30 #include "media/JetPlayer.h"
     31 
     32 
     33 using namespace android;
     34 
     35 // ----------------------------------------------------------------------------
     36 static const char* const kClassPathName = "android/media/JetPlayer";
     37 
     38 // ----------------------------------------------------------------------------
     39 struct fields_t {
     40     // these fields provide access from C++ to the...
     41     jclass    jetClass;              // JetPlayer java class global ref
     42     jmethodID postNativeEventInJava; // java method to post events to the Java thread from native
     43     jfieldID  nativePlayerInJavaObj; // stores in Java the native JetPlayer object
     44 };
     45 
     46 static fields_t javaJetPlayerFields;
     47 
     48 
     49 // ----------------------------------------------------------------------------
     50 // ----------------------------------------------------------------------------
     51 
     52 /*
     53  * This function is called from JetPlayer instance's render thread
     54  */
     55 static void
     56 jetPlayerEventCallback(int what, int arg1=0, int arg2=0, void* javaTarget = NULL)
     57 {
     58     JNIEnv *env = AndroidRuntime::getJNIEnv();
     59     if(env) {
     60         env->CallStaticVoidMethod(
     61             javaJetPlayerFields.jetClass, javaJetPlayerFields.postNativeEventInJava,
     62             javaTarget,
     63             what, arg1, arg2);
     64         if (env->ExceptionCheck()) {
     65             env->ExceptionDescribe();
     66             env->ExceptionClear();
     67         }
     68     } else {
     69         LOGE("JET jetPlayerEventCallback(): No JNI env for JET event callback, can't post event.");
     70         return;
     71     }
     72 }
     73 
     74 
     75 // ----------------------------------------------------------------------------
     76 // ----------------------------------------------------------------------------
     77 
     78 static jboolean
     79 android_media_JetPlayer_setup(JNIEnv *env, jobject thiz, jobject weak_this,
     80     jint maxTracks, jint trackBufferSize)
     81 {
     82     //LOGV("android_media_JetPlayer_setup(): entering.");
     83     JetPlayer* lpJet = new JetPlayer(env->NewGlobalRef(weak_this), maxTracks, trackBufferSize);
     84 
     85     EAS_RESULT result = lpJet->init();
     86 
     87     if(result==EAS_SUCCESS) {
     88         // save our newly created C++ JetPlayer in the "nativePlayerInJavaObj" field
     89         // of the Java object (in mNativePlayerInJavaObj)
     90         env->SetIntField(thiz, javaJetPlayerFields.nativePlayerInJavaObj, (int)lpJet);
     91         return JNI_TRUE;
     92     } else {
     93         LOGE("android_media_JetPlayer_setup(): initialization failed with EAS error code %d", (int)result);
     94         delete lpJet;
     95         env->SetIntField(weak_this, javaJetPlayerFields.nativePlayerInJavaObj, 0);
     96         return JNI_FALSE;
     97     }
     98 }
     99 
    100 
    101 // ----------------------------------------------------------------------------
    102 static void
    103 android_media_JetPlayer_finalize(JNIEnv *env, jobject thiz)
    104 {
    105     LOGV("android_media_JetPlayer_finalize(): entering.");
    106     JetPlayer *lpJet = (JetPlayer *)env->GetIntField(
    107         thiz, javaJetPlayerFields.nativePlayerInJavaObj);
    108     if(lpJet != NULL) {
    109         lpJet->release();
    110         delete lpJet;
    111     }
    112 
    113     LOGV("android_media_JetPlayer_finalize(): exiting.");
    114 }
    115 
    116 
    117 // ----------------------------------------------------------------------------
    118 static void
    119 android_media_JetPlayer_release(JNIEnv *env, jobject thiz)
    120 {
    121     android_media_JetPlayer_finalize(env, thiz);
    122     env->SetIntField(thiz, javaJetPlayerFields.nativePlayerInJavaObj, 0);
    123     LOGV("android_media_JetPlayer_release() done");
    124 }
    125 
    126 
    127 // ----------------------------------------------------------------------------
    128 static jboolean
    129 android_media_JetPlayer_loadFromFile(JNIEnv *env, jobject thiz, jstring path)
    130 {
    131     JetPlayer *lpJet = (JetPlayer *)env->GetIntField(
    132         thiz, javaJetPlayerFields.nativePlayerInJavaObj);
    133     if (lpJet == NULL ) {
    134         jniThrowException(env, "java/lang/IllegalStateException",
    135             "Unable to retrieve JetPlayer pointer for openFile()");
    136     }
    137 
    138     // set up event callback function
    139     lpJet->setEventCallback(jetPlayerEventCallback);
    140 
    141     const char *pathStr = env->GetStringUTFChars(path, NULL);
    142     if (pathStr == NULL) {  // Out of memory
    143         LOGE("android_media_JetPlayer_openFile(): aborting, out of memory");
    144         jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
    145         return JNI_FALSE;
    146     }
    147 
    148     LOGV("android_media_JetPlayer_openFile(): trying to open %s", pathStr );
    149     EAS_RESULT result = lpJet->loadFromFile(pathStr);
    150     env->ReleaseStringUTFChars(path, pathStr);
    151 
    152     if(result==EAS_SUCCESS) {
    153         //LOGV("android_media_JetPlayer_openFile(): file successfully opened");
    154         return JNI_TRUE;
    155     } else {
    156         LOGE("android_media_JetPlayer_openFile(): failed to open file with EAS error %d",
    157             (int)result);
    158         return JNI_FALSE;
    159     }
    160 }
    161 
    162 
    163 // ----------------------------------------------------------------------------
    164 static jboolean
    165 android_media_JetPlayer_loadFromFileD(JNIEnv *env, jobject thiz,
    166     jobject fileDescriptor, jlong offset, jlong length)
    167 {
    168     JetPlayer *lpJet = (JetPlayer *)env->GetIntField(
    169         thiz, javaJetPlayerFields.nativePlayerInJavaObj);
    170     if (lpJet == NULL ) {
    171         jniThrowException(env, "java/lang/IllegalStateException",
    172             "Unable to retrieve JetPlayer pointer for openFile()");
    173     }
    174 
    175     // set up event callback function
    176     lpJet->setEventCallback(jetPlayerEventCallback);
    177 
    178     LOGV("android_media_JetPlayer_openFileDescr(): trying to load JET file through its fd" );
    179     EAS_RESULT result = lpJet->loadFromFD(getParcelFileDescriptorFD(env, fileDescriptor),
    180         (long long)offset, (long long)length); // cast params to types used by EAS_FILE
    181 
    182     if(result==EAS_SUCCESS) {
    183         LOGV("android_media_JetPlayer_openFileDescr(): file successfully opened");
    184         return JNI_TRUE;
    185     } else {
    186         LOGE("android_media_JetPlayer_openFileDescr(): failed to open file with EAS error %d",
    187             (int)result);
    188         return JNI_FALSE;
    189     }
    190 }
    191 
    192 
    193 // ----------------------------------------------------------------------------
    194 static jboolean
    195 android_media_JetPlayer_closeFile(JNIEnv *env, jobject thiz)
    196 {
    197     JetPlayer *lpJet = (JetPlayer *)env->GetIntField(
    198         thiz, javaJetPlayerFields.nativePlayerInJavaObj);
    199     if (lpJet == NULL ) {
    200         jniThrowException(env, "java/lang/IllegalStateException",
    201             "Unable to retrieve JetPlayer pointer for closeFile()");
    202     }
    203 
    204     if( lpJet->closeFile()==EAS_SUCCESS) {
    205         //LOGV("android_media_JetPlayer_closeFile(): file successfully closed");
    206         return JNI_TRUE;
    207     } else {
    208         LOGE("android_media_JetPlayer_closeFile(): failed to close file");
    209         return JNI_FALSE;
    210     }
    211 }
    212 
    213 
    214 // ----------------------------------------------------------------------------
    215 static jboolean
    216 android_media_JetPlayer_play(JNIEnv *env, jobject thiz)
    217 {
    218     JetPlayer *lpJet = (JetPlayer *)env->GetIntField(
    219         thiz, javaJetPlayerFields.nativePlayerInJavaObj);
    220     if (lpJet == NULL ) {
    221         jniThrowException(env, "java/lang/IllegalStateException",
    222             "Unable to retrieve JetPlayer pointer for play()");
    223     }
    224 
    225     EAS_RESULT result = lpJet->play();
    226     if( result==EAS_SUCCESS) {
    227         //LOGV("android_media_JetPlayer_play(): play successful");
    228         return JNI_TRUE;
    229     } else {
    230         LOGE("android_media_JetPlayer_play(): failed to play with EAS error code %ld",
    231             result);
    232         return JNI_FALSE;
    233     }
    234 }
    235 
    236 
    237 // ----------------------------------------------------------------------------
    238 static jboolean
    239 android_media_JetPlayer_pause(JNIEnv *env, jobject thiz)
    240 {
    241     JetPlayer *lpJet = (JetPlayer *)env->GetIntField(
    242         thiz, javaJetPlayerFields.nativePlayerInJavaObj);
    243     if (lpJet == NULL ) {
    244         jniThrowException(env, "java/lang/IllegalStateException",
    245             "Unable to retrieve JetPlayer pointer for pause()");
    246     }
    247 
    248     EAS_RESULT result = lpJet->pause();
    249     if( result==EAS_SUCCESS) {
    250         //LOGV("android_media_JetPlayer_pause(): pause successful");
    251         return JNI_TRUE;
    252     } else {
    253         if(result==EAS_ERROR_QUEUE_IS_EMPTY) {
    254             LOGV("android_media_JetPlayer_pause(): paused with an empty queue");
    255             return JNI_TRUE;
    256         } else
    257             LOGE("android_media_JetPlayer_pause(): failed to pause with EAS error code %ld",
    258                 result);
    259         return JNI_FALSE;
    260     }
    261 }
    262 
    263 
    264 // ----------------------------------------------------------------------------
    265 static jboolean
    266 android_media_JetPlayer_queueSegment(JNIEnv *env, jobject thiz,
    267         jint segmentNum, jint libNum, jint repeatCount, jint transpose, jint muteFlags,
    268         jbyte userID)
    269 {
    270     JetPlayer *lpJet = (JetPlayer *)env->GetIntField(
    271         thiz, javaJetPlayerFields.nativePlayerInJavaObj);
    272     if (lpJet == NULL ) {
    273         jniThrowException(env, "java/lang/IllegalStateException",
    274             "Unable to retrieve JetPlayer pointer for queueSegment()");
    275     }
    276 
    277     EAS_RESULT result
    278         = lpJet->queueSegment(segmentNum, libNum, repeatCount, transpose, muteFlags, userID);
    279     if(result==EAS_SUCCESS) {
    280         //LOGV("android_media_JetPlayer_queueSegment(): segment successfully queued");
    281         return JNI_TRUE;
    282     } else {
    283         LOGE("android_media_JetPlayer_queueSegment(): failed with EAS error code %ld",
    284             result);
    285         return JNI_FALSE;
    286     }
    287 }
    288 
    289 
    290 // ----------------------------------------------------------------------------
    291 static jboolean
    292 android_media_JetPlayer_queueSegmentMuteArray(JNIEnv *env, jobject thiz,
    293         jint segmentNum, jint libNum, jint repeatCount, jint transpose, jbooleanArray muteArray,
    294         jbyte userID)
    295 {
    296     JetPlayer *lpJet = (JetPlayer *)env->GetIntField(
    297         thiz, javaJetPlayerFields.nativePlayerInJavaObj);
    298     if (lpJet == NULL ) {
    299         jniThrowException(env, "java/lang/IllegalStateException",
    300             "Unable to retrieve JetPlayer pointer for queueSegmentMuteArray()");
    301     }
    302 
    303     EAS_RESULT result=EAS_FAILURE;
    304 
    305     jboolean *muteTracks = NULL;
    306     muteTracks = env->GetBooleanArrayElements(muteArray, NULL);
    307     if (muteTracks == NULL) {
    308         LOGE("android_media_JetPlayer_queueSegment(): failed to read track mute mask.");
    309         return JNI_FALSE;
    310     }
    311 
    312     EAS_U32 muteMask=0;
    313     int maxTracks = lpJet->getMaxTracks();
    314     for (jint trackIndex=0; trackIndex<maxTracks; trackIndex++) {
    315         if(muteTracks[maxTracks-1-trackIndex]==JNI_TRUE)
    316             muteMask = (muteMask << 1) | 0x00000001;
    317         else
    318             muteMask = muteMask << 1;
    319     }
    320     //LOGV("android_media_JetPlayer_queueSegmentMuteArray(): FINAL mute mask =0x%08lX", mask);
    321 
    322     result = lpJet->queueSegment(segmentNum, libNum, repeatCount, transpose, muteMask, userID);
    323 
    324     env->ReleaseBooleanArrayElements(muteArray, muteTracks, 0);
    325     if(result==EAS_SUCCESS) {
    326         //LOGV("android_media_JetPlayer_queueSegmentMuteArray(): segment successfully queued");
    327         return JNI_TRUE;
    328     } else {
    329         LOGE("android_media_JetPlayer_queueSegmentMuteArray(): failed with EAS error code %ld",
    330             result);
    331         return JNI_FALSE;
    332     }
    333 }
    334 
    335 
    336 // ----------------------------------------------------------------------------
    337 static jboolean
    338 android_media_JetPlayer_setMuteFlags(JNIEnv *env, jobject thiz,
    339          jint muteFlags /*unsigned?*/, jboolean bSync)
    340 {
    341     JetPlayer *lpJet = (JetPlayer *)env->GetIntField(
    342         thiz, javaJetPlayerFields.nativePlayerInJavaObj);
    343     if (lpJet == NULL ) {
    344         jniThrowException(env, "java/lang/IllegalStateException",
    345             "Unable to retrieve JetPlayer pointer for setMuteFlags()");
    346     }
    347 
    348     EAS_RESULT result;
    349     result = lpJet->setMuteFlags(muteFlags, bSync==JNI_TRUE ? true : false);
    350     if(result==EAS_SUCCESS) {
    351         //LOGV("android_media_JetPlayer_setMuteFlags(): mute flags successfully updated");
    352         return JNI_TRUE;
    353     } else {
    354         LOGE("android_media_JetPlayer_setMuteFlags(): failed with EAS error code %ld", result);
    355         return JNI_FALSE;
    356     }
    357 }
    358 
    359 
    360 // ----------------------------------------------------------------------------
    361 static jboolean
    362 android_media_JetPlayer_setMuteArray(JNIEnv *env, jobject thiz,
    363         jbooleanArray muteArray, jboolean bSync)
    364 {
    365     JetPlayer *lpJet = (JetPlayer *)env->GetIntField(
    366         thiz, javaJetPlayerFields.nativePlayerInJavaObj);
    367     if (lpJet == NULL ) {
    368         jniThrowException(env, "java/lang/IllegalStateException",
    369             "Unable to retrieve JetPlayer pointer for setMuteArray()");
    370     }
    371 
    372     EAS_RESULT result=EAS_FAILURE;
    373 
    374     jboolean *muteTracks = NULL;
    375     muteTracks = env->GetBooleanArrayElements(muteArray, NULL);
    376     if (muteTracks == NULL) {
    377         LOGE("android_media_JetPlayer_setMuteArray(): failed to read track mute mask.");
    378         return JNI_FALSE;
    379     }
    380 
    381     EAS_U32 muteMask=0;
    382     int maxTracks = lpJet->getMaxTracks();
    383     for (jint trackIndex=0; trackIndex<maxTracks; trackIndex++) {
    384         if(muteTracks[maxTracks-1-trackIndex]==JNI_TRUE)
    385             muteMask = (muteMask << 1) | 0x00000001;
    386         else
    387             muteMask = muteMask << 1;
    388     }
    389     //LOGV("android_media_JetPlayer_setMuteArray(): FINAL mute mask =0x%08lX", muteMask);
    390 
    391     result = lpJet->setMuteFlags(muteMask, bSync==JNI_TRUE ? true : false);
    392 
    393     env->ReleaseBooleanArrayElements(muteArray, muteTracks, 0);
    394     if(result==EAS_SUCCESS) {
    395         //LOGV("android_media_JetPlayer_setMuteArray(): mute flags successfully updated");
    396         return JNI_TRUE;
    397     } else {
    398         LOGE("android_media_JetPlayer_setMuteArray(): \
    399             failed to update mute flags with EAS error code %ld", result);
    400         return JNI_FALSE;
    401     }
    402 }
    403 
    404 
    405 // ----------------------------------------------------------------------------
    406 static jboolean
    407 android_media_JetPlayer_setMuteFlag(JNIEnv *env, jobject thiz,
    408          jint trackId, jboolean muteFlag, jboolean bSync)
    409 {
    410     JetPlayer *lpJet = (JetPlayer *)env->GetIntField(
    411         thiz, javaJetPlayerFields.nativePlayerInJavaObj);
    412     if (lpJet == NULL ) {
    413         jniThrowException(env, "java/lang/IllegalStateException",
    414             "Unable to retrieve JetPlayer pointer for setMuteFlag()");
    415     }
    416 
    417     EAS_RESULT result;
    418     result = lpJet->setMuteFlag(trackId,
    419         muteFlag==JNI_TRUE ? true : false, bSync==JNI_TRUE ? true : false);
    420     if(result==EAS_SUCCESS) {
    421         //LOGV("android_media_JetPlayer_setMuteFlag(): mute flag successfully updated for track %d", trackId);
    422         return JNI_TRUE;
    423     } else {
    424         LOGE("android_media_JetPlayer_setMuteFlag(): failed to update mute flag for track %d with EAS error code %ld",
    425                 trackId, result);
    426         return JNI_FALSE;
    427     }
    428 }
    429 
    430 
    431 // ----------------------------------------------------------------------------
    432 static jboolean
    433 android_media_JetPlayer_triggerClip(JNIEnv *env, jobject thiz, jint clipId)
    434 {
    435     JetPlayer *lpJet = (JetPlayer *)env->GetIntField(
    436         thiz, javaJetPlayerFields.nativePlayerInJavaObj);
    437     if (lpJet == NULL ) {
    438         jniThrowException(env, "java/lang/IllegalStateException",
    439             "Unable to retrieve JetPlayer pointer for triggerClip()");
    440     }
    441 
    442     EAS_RESULT result;
    443     result = lpJet->triggerClip(clipId);
    444     if(result==EAS_SUCCESS) {
    445         //LOGV("android_media_JetPlayer_triggerClip(): triggerClip successful for clip %d", clipId);
    446         return JNI_TRUE;
    447     } else {
    448         LOGE("android_media_JetPlayer_triggerClip(): triggerClip for clip %d failed with EAS error code %ld",
    449                 clipId, result);
    450         return JNI_FALSE;
    451     }
    452 }
    453 
    454 
    455 // ----------------------------------------------------------------------------
    456 static jboolean
    457 android_media_JetPlayer_clearQueue(JNIEnv *env, jobject thiz)
    458 {
    459     JetPlayer *lpJet = (JetPlayer *)env->GetIntField(
    460         thiz, javaJetPlayerFields.nativePlayerInJavaObj);
    461     if (lpJet == NULL ) {
    462         jniThrowException(env, "java/lang/IllegalStateException",
    463             "Unable to retrieve JetPlayer pointer for clearQueue()");
    464     }
    465 
    466     EAS_RESULT result = lpJet->clearQueue();
    467     if(result==EAS_SUCCESS) {
    468         //LOGV("android_media_JetPlayer_clearQueue(): clearQueue successful");
    469         return JNI_TRUE;
    470     } else {
    471         LOGE("android_media_JetPlayer_clearQueue(): clearQueue failed with EAS error code %ld",
    472                 result);
    473         return JNI_FALSE;
    474     }
    475 }
    476 
    477 
    478 // ----------------------------------------------------------------------------
    479 // ----------------------------------------------------------------------------
    480 static JNINativeMethod gMethods[] = {
    481     // name,               signature,               funcPtr
    482     {"native_setup",       "(Ljava/lang/Object;II)Z", (void *)android_media_JetPlayer_setup},
    483     {"native_finalize",    "()V",                   (void *)android_media_JetPlayer_finalize},
    484     {"native_release",     "()V",                   (void *)android_media_JetPlayer_release},
    485     {"native_loadJetFromFile",
    486                            "(Ljava/lang/String;)Z", (void *)android_media_JetPlayer_loadFromFile},
    487     {"native_loadJetFromFileD", "(Ljava/io/FileDescriptor;JJ)Z",
    488                                                     (void *)android_media_JetPlayer_loadFromFileD},
    489     {"native_closeJetFile","()Z",                   (void *)android_media_JetPlayer_closeFile},
    490     {"native_playJet",     "()Z",                   (void *)android_media_JetPlayer_play},
    491     {"native_pauseJet",    "()Z",                   (void *)android_media_JetPlayer_pause},
    492     {"native_queueJetSegment",
    493                            "(IIIIIB)Z",             (void *)android_media_JetPlayer_queueSegment},
    494     {"native_queueJetSegmentMuteArray",
    495                            "(IIII[ZB)Z",     (void *)android_media_JetPlayer_queueSegmentMuteArray},
    496     {"native_setMuteFlags","(IZ)Z",                 (void *)android_media_JetPlayer_setMuteFlags},
    497     {"native_setMuteArray","([ZZ)Z",                (void *)android_media_JetPlayer_setMuteArray},
    498     {"native_setMuteFlag", "(IZZ)Z",                (void *)android_media_JetPlayer_setMuteFlag},
    499     {"native_triggerClip", "(I)Z",                  (void *)android_media_JetPlayer_triggerClip},
    500     {"native_clearQueue",  "()Z",                   (void *)android_media_JetPlayer_clearQueue},
    501 };
    502 
    503 #define JAVA_NATIVEJETPLAYERINJAVAOBJ_FIELD_NAME "mNativePlayerInJavaObj"
    504 #define JAVA_NATIVEJETPOSTEVENT_CALLBACK_NAME "postEventFromNative"
    505 
    506 
    507 int register_android_media_JetPlayer(JNIEnv *env)
    508 {
    509     jclass jetPlayerClass = NULL;
    510     javaJetPlayerFields.jetClass = NULL;
    511     javaJetPlayerFields.postNativeEventInJava = NULL;
    512     javaJetPlayerFields.nativePlayerInJavaObj = NULL;
    513 
    514     // Get the JetPlayer java class
    515     jetPlayerClass = env->FindClass(kClassPathName);
    516     if (jetPlayerClass == NULL) {
    517         LOGE("Can't find %s", kClassPathName);
    518         return -1;
    519     }
    520     javaJetPlayerFields.jetClass = (jclass)env->NewGlobalRef(jetPlayerClass);
    521 
    522     // Get the mNativePlayerInJavaObj variable field
    523     javaJetPlayerFields.nativePlayerInJavaObj = env->GetFieldID(
    524             jetPlayerClass,
    525             JAVA_NATIVEJETPLAYERINJAVAOBJ_FIELD_NAME, "I");
    526     if (javaJetPlayerFields.nativePlayerInJavaObj == NULL) {
    527         LOGE("Can't find AudioTrack.%s", JAVA_NATIVEJETPLAYERINJAVAOBJ_FIELD_NAME);
    528         return -1;
    529     }
    530 
    531     // Get the callback to post events from this native code to Java
    532     javaJetPlayerFields.postNativeEventInJava = env->GetStaticMethodID(javaJetPlayerFields.jetClass,
    533             JAVA_NATIVEJETPOSTEVENT_CALLBACK_NAME, "(Ljava/lang/Object;III)V");
    534     if (javaJetPlayerFields.postNativeEventInJava == NULL) {
    535         LOGE("Can't find Jet.%s", JAVA_NATIVEJETPOSTEVENT_CALLBACK_NAME);
    536         return -1;
    537     }
    538 
    539     return AndroidRuntime::registerNativeMethods(env,
    540             kClassPathName, gMethods, NELEM(gMethods));
    541 }
    542