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 
     19 #define LOG_TAG "AudioRecord-JNI"
     20 
     21 #include <stdio.h>
     22 #include <unistd.h>
     23 #include <fcntl.h>
     24 #include <math.h>
     25 
     26 #include <jni.h>
     27 #include <JNIHelp.h>
     28 #include <android_runtime/AndroidRuntime.h>
     29 
     30 #include <utils/Log.h>
     31 #include <utils/SortedVector.h>
     32 #include <utils/threads.h>
     33 #include <media/AudioRecord.h>
     34 #include <media/mediarecorder.h>
     35 
     36 #include <cutils/bitops.h>
     37 
     38 #include <system/audio.h>
     39 
     40 // ----------------------------------------------------------------------------
     41 
     42 using namespace android;
     43 
     44 // ----------------------------------------------------------------------------
     45 static const char* const kClassPathName = "android/media/AudioRecord";
     46 
     47 struct fields_t {
     48     // these fields provide access from C++ to the...
     49     jmethodID postNativeEventInJava; //... event post callback method
     50     int       PCM16;                 //...  format constants
     51     int       PCM8;                  //...  format constants
     52     jfieldID  nativeRecorderInJavaObj; // provides access to the C++ AudioRecord object
     53     jfieldID  nativeCallbackCookie;    // provides access to the AudioRecord callback data
     54 };
     55 static fields_t javaAudioRecordFields;
     56 
     57 struct audiorecord_callback_cookie {
     58     jclass      audioRecord_class;
     59     jobject     audioRecord_ref;
     60     bool        busy;
     61     Condition   cond;
     62 };
     63 
     64 static Mutex sLock;
     65 static SortedVector <audiorecord_callback_cookie *> sAudioRecordCallBackCookies;
     66 
     67 // ----------------------------------------------------------------------------
     68 
     69 #define AUDIORECORD_SUCCESS                         0
     70 #define AUDIORECORD_ERROR                           -1
     71 #define AUDIORECORD_ERROR_BAD_VALUE                 -2
     72 #define AUDIORECORD_ERROR_INVALID_OPERATION         -3
     73 #define AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT      -16
     74 #define AUDIORECORD_ERROR_SETUP_INVALIDCHANNELMASK -17
     75 #define AUDIORECORD_ERROR_SETUP_INVALIDFORMAT       -18
     76 #define AUDIORECORD_ERROR_SETUP_INVALIDSOURCE       -19
     77 #define AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED    -20
     78 
     79 jint android_media_translateRecorderErrorCode(int code) {
     80     switch (code) {
     81     case NO_ERROR:
     82         return AUDIORECORD_SUCCESS;
     83     case BAD_VALUE:
     84         return AUDIORECORD_ERROR_BAD_VALUE;
     85     case INVALID_OPERATION:
     86         return AUDIORECORD_ERROR_INVALID_OPERATION;
     87     default:
     88         return AUDIORECORD_ERROR;
     89     }
     90 }
     91 
     92 
     93 // ----------------------------------------------------------------------------
     94 static void recorderCallback(int event, void* user, void *info) {
     95 
     96     audiorecord_callback_cookie *callbackInfo = (audiorecord_callback_cookie *)user;
     97     {
     98         Mutex::Autolock l(sLock);
     99         if (sAudioRecordCallBackCookies.indexOf(callbackInfo) < 0) {
    100             return;
    101         }
    102         callbackInfo->busy = true;
    103     }
    104     if (event == AudioRecord::EVENT_MORE_DATA) {
    105         // set size to 0 to signal we're not using the callback to read more data
    106         AudioRecord::Buffer* pBuff = (AudioRecord::Buffer*)info;
    107         pBuff->size = 0;
    108 
    109     } else if (event == AudioRecord::EVENT_MARKER) {
    110         JNIEnv *env = AndroidRuntime::getJNIEnv();
    111         if (user && env) {
    112             env->CallStaticVoidMethod(
    113                 callbackInfo->audioRecord_class,
    114                 javaAudioRecordFields.postNativeEventInJava,
    115                 callbackInfo->audioRecord_ref, event, 0,0, NULL);
    116             if (env->ExceptionCheck()) {
    117                 env->ExceptionDescribe();
    118                 env->ExceptionClear();
    119             }
    120         }
    121 
    122     } else if (event == AudioRecord::EVENT_NEW_POS) {
    123         JNIEnv *env = AndroidRuntime::getJNIEnv();
    124         if (user && env) {
    125             env->CallStaticVoidMethod(
    126                 callbackInfo->audioRecord_class,
    127                 javaAudioRecordFields.postNativeEventInJava,
    128                 callbackInfo->audioRecord_ref, event, 0,0, NULL);
    129             if (env->ExceptionCheck()) {
    130                 env->ExceptionDescribe();
    131                 env->ExceptionClear();
    132             }
    133         }
    134     }
    135     {
    136         Mutex::Autolock l(sLock);
    137         callbackInfo->busy = false;
    138         callbackInfo->cond.broadcast();
    139     }
    140 }
    141 
    142 // ----------------------------------------------------------------------------
    143 static sp<AudioRecord> getAudioRecord(JNIEnv* env, jobject thiz)
    144 {
    145     Mutex::Autolock l(sLock);
    146     AudioRecord* const ar =
    147             (AudioRecord*)env->GetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
    148     return sp<AudioRecord>(ar);
    149 }
    150 
    151 static sp<AudioRecord> setAudioRecord(JNIEnv* env, jobject thiz, const sp<AudioRecord>& ar)
    152 {
    153     Mutex::Autolock l(sLock);
    154     sp<AudioRecord> old =
    155             (AudioRecord*)env->GetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
    156     if (ar.get()) {
    157         ar->incStrong(thiz);
    158     }
    159     if (old != 0) {
    160         old->decStrong(thiz);
    161     }
    162     env->SetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj, (int)ar.get());
    163     return old;
    164 }
    165 
    166 // ----------------------------------------------------------------------------
    167 static int
    168 android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject weak_this,
    169         jint source, jint sampleRateInHertz, jint channels,
    170         jint audioFormat, jint buffSizeInBytes, jintArray jSession)
    171 {
    172     //ALOGV(">> Entering android_media_AudioRecord_setup");
    173     //ALOGV("sampleRate=%d, audioFormat=%d, channels=%x, buffSizeInBytes=%d",
    174     //     sampleRateInHertz, audioFormat, channels,     buffSizeInBytes);
    175 
    176     if (!audio_is_input_channel(channels)) {
    177         ALOGE("Error creating AudioRecord: channel count is not 1 or 2.");
    178         return AUDIORECORD_ERROR_SETUP_INVALIDCHANNELMASK;
    179     }
    180     uint32_t nbChannels = popcount(channels);
    181 
    182     // compare the format against the Java constants
    183     if ((audioFormat != javaAudioRecordFields.PCM16)
    184         && (audioFormat != javaAudioRecordFields.PCM8)) {
    185         ALOGE("Error creating AudioRecord: unsupported audio format.");
    186         return AUDIORECORD_ERROR_SETUP_INVALIDFORMAT;
    187     }
    188 
    189     int bytesPerSample = audioFormat==javaAudioRecordFields.PCM16 ? 2 : 1;
    190     audio_format_t format = audioFormat==javaAudioRecordFields.PCM16 ?
    191             AUDIO_FORMAT_PCM_16_BIT : AUDIO_FORMAT_PCM_8_BIT;
    192 
    193     if (buffSizeInBytes == 0) {
    194          ALOGE("Error creating AudioRecord: frameCount is 0.");
    195         return AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT;
    196     }
    197     int frameSize = nbChannels * bytesPerSample;
    198     size_t frameCount = buffSizeInBytes / frameSize;
    199 
    200     if (uint32_t(source) >= AUDIO_SOURCE_CNT) {
    201         ALOGE("Error creating AudioRecord: unknown source.");
    202         return AUDIORECORD_ERROR_SETUP_INVALIDSOURCE;
    203     }
    204 
    205     jclass clazz = env->GetObjectClass(thiz);
    206     if (clazz == NULL) {
    207         ALOGE("Can't find %s when setting up callback.", kClassPathName);
    208         return AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED;
    209     }
    210 
    211     if (jSession == NULL) {
    212         ALOGE("Error creating AudioRecord: invalid session ID pointer");
    213         return AUDIORECORD_ERROR;
    214     }
    215 
    216     jint* nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
    217     if (nSession == NULL) {
    218         ALOGE("Error creating AudioRecord: Error retrieving session id pointer");
    219         return AUDIORECORD_ERROR;
    220     }
    221     int sessionId = nSession[0];
    222     env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
    223     nSession = NULL;
    224 
    225     // create an uninitialized AudioRecord object
    226     sp<AudioRecord> lpRecorder = new AudioRecord();
    227     if (lpRecorder == NULL) {
    228         ALOGE("Error creating AudioRecord instance.");
    229         return AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED;
    230     }
    231 
    232     // create the callback information:
    233     // this data will be passed with every AudioRecord callback
    234     audiorecord_callback_cookie *lpCallbackData = new audiorecord_callback_cookie;
    235     lpCallbackData->audioRecord_class = (jclass)env->NewGlobalRef(clazz);
    236     // we use a weak reference so the AudioRecord object can be garbage collected.
    237     lpCallbackData->audioRecord_ref = env->NewGlobalRef(weak_this);
    238     lpCallbackData->busy = false;
    239 
    240     lpRecorder->set((audio_source_t) source,
    241         sampleRateInHertz,
    242         format,        // word length, PCM
    243         channels,
    244         frameCount,
    245         (AudioRecord::record_flags) 0,  // flags
    246         recorderCallback,// callback_t
    247         lpCallbackData,// void* user
    248         0,             // notificationFrames,
    249         true,          // threadCanCallJava)
    250         sessionId);
    251 
    252     if (lpRecorder->initCheck() != NO_ERROR) {
    253         ALOGE("Error creating AudioRecord instance: initialization check failed.");
    254         goto native_init_failure;
    255     }
    256 
    257     nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
    258     if (nSession == NULL) {
    259         ALOGE("Error creating AudioRecord: Error retrieving session id pointer");
    260         goto native_init_failure;
    261     }
    262     // read the audio session ID back from AudioRecord in case a new session was created during set()
    263     nSession[0] = lpRecorder->getSessionId();
    264     env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
    265     nSession = NULL;
    266 
    267     {   // scope for the lock
    268         Mutex::Autolock l(sLock);
    269         sAudioRecordCallBackCookies.add(lpCallbackData);
    270     }
    271     // save our newly created C++ AudioRecord in the "nativeRecorderInJavaObj" field
    272     // of the Java object
    273     setAudioRecord(env, thiz, lpRecorder);
    274 
    275     // save our newly created callback information in the "nativeCallbackCookie" field
    276     // of the Java object (in mNativeCallbackCookie) so we can free the memory in finalize()
    277     env->SetIntField(thiz, javaAudioRecordFields.nativeCallbackCookie, (int)lpCallbackData);
    278 
    279     return AUDIORECORD_SUCCESS;
    280 
    281     // failure:
    282 native_init_failure:
    283     env->DeleteGlobalRef(lpCallbackData->audioRecord_class);
    284     env->DeleteGlobalRef(lpCallbackData->audioRecord_ref);
    285     delete lpCallbackData;
    286     env->SetIntField(thiz, javaAudioRecordFields.nativeCallbackCookie, 0);
    287 
    288     return AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED;
    289 }
    290 
    291 
    292 
    293 // ----------------------------------------------------------------------------
    294 static int
    295 android_media_AudioRecord_start(JNIEnv *env, jobject thiz, jint event, jint triggerSession)
    296 {
    297     sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
    298     if (lpRecorder == NULL ) {
    299         jniThrowException(env, "java/lang/IllegalStateException", NULL);
    300         return AUDIORECORD_ERROR;
    301     }
    302 
    303     return android_media_translateRecorderErrorCode(
    304             lpRecorder->start((AudioSystem::sync_event_t)event, triggerSession));
    305 }
    306 
    307 
    308 // ----------------------------------------------------------------------------
    309 static void
    310 android_media_AudioRecord_stop(JNIEnv *env, jobject thiz)
    311 {
    312     sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
    313     if (lpRecorder == NULL ) {
    314         jniThrowException(env, "java/lang/IllegalStateException", NULL);
    315         return;
    316     }
    317 
    318     lpRecorder->stop();
    319     //ALOGV("Called lpRecorder->stop()");
    320 }
    321 
    322 
    323 // ----------------------------------------------------------------------------
    324 
    325 #define CALLBACK_COND_WAIT_TIMEOUT_MS 1000
    326 static void android_media_AudioRecord_release(JNIEnv *env,  jobject thiz) {
    327     sp<AudioRecord> lpRecorder = setAudioRecord(env, thiz, 0);
    328     if (lpRecorder == NULL) {
    329         return;
    330     }
    331     ALOGV("About to delete lpRecorder: %x\n", (int)lpRecorder.get());
    332     lpRecorder->stop();
    333 
    334     audiorecord_callback_cookie *lpCookie = (audiorecord_callback_cookie *)env->GetIntField(
    335         thiz, javaAudioRecordFields.nativeCallbackCookie);
    336 
    337     // reset the native resources in the Java object so any attempt to access
    338     // them after a call to release fails.
    339     env->SetIntField(thiz, javaAudioRecordFields.nativeCallbackCookie, 0);
    340 
    341     // delete the callback information
    342     if (lpCookie) {
    343         Mutex::Autolock l(sLock);
    344         ALOGV("deleting lpCookie: %x\n", (int)lpCookie);
    345         while (lpCookie->busy) {
    346             if (lpCookie->cond.waitRelative(sLock,
    347                                             milliseconds(CALLBACK_COND_WAIT_TIMEOUT_MS)) !=
    348                                                     NO_ERROR) {
    349                 break;
    350             }
    351         }
    352         sAudioRecordCallBackCookies.remove(lpCookie);
    353         env->DeleteGlobalRef(lpCookie->audioRecord_class);
    354         env->DeleteGlobalRef(lpCookie->audioRecord_ref);
    355         delete lpCookie;
    356     }
    357 }
    358 
    359 
    360 // ----------------------------------------------------------------------------
    361 static void android_media_AudioRecord_finalize(JNIEnv *env,  jobject thiz) {
    362     android_media_AudioRecord_release(env, thiz);
    363 }
    364 
    365 
    366 // ----------------------------------------------------------------------------
    367 static jint android_media_AudioRecord_readInByteArray(JNIEnv *env,  jobject thiz,
    368                                                         jbyteArray javaAudioData,
    369                                                         jint offsetInBytes, jint sizeInBytes) {
    370     jbyte* recordBuff = NULL;
    371     // get the audio recorder from which we'll read new audio samples
    372     sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
    373     if (lpRecorder == NULL) {
    374         ALOGE("Unable to retrieve AudioRecord object, can't record");
    375         return 0;
    376     }
    377 
    378     if (!javaAudioData) {
    379         ALOGE("Invalid Java array to store recorded audio, can't record");
    380         return 0;
    381     }
    382 
    383     // get the pointer to where we'll record the audio
    384     // NOTE: We may use GetPrimitiveArrayCritical() when the JNI implementation changes in such
    385     // a way that it becomes much more efficient. When doing so, we will have to prevent the
    386     // AudioSystem callback to be called while in critical section (in case of media server
    387     // process crash for instance)
    388     recordBuff = (jbyte *)env->GetByteArrayElements(javaAudioData, NULL);
    389 
    390     if (recordBuff == NULL) {
    391         ALOGE("Error retrieving destination for recorded audio data, can't record");
    392         return 0;
    393     }
    394 
    395     // read the new audio data from the native AudioRecord object
    396     ssize_t recorderBuffSize = lpRecorder->frameCount()*lpRecorder->frameSize();
    397     ssize_t readSize = lpRecorder->read(recordBuff + offsetInBytes,
    398                                         sizeInBytes > (jint)recorderBuffSize ?
    399                                             (jint)recorderBuffSize : sizeInBytes );
    400     env->ReleaseByteArrayElements(javaAudioData, recordBuff, 0);
    401 
    402     return (jint) readSize;
    403 }
    404 
    405 // ----------------------------------------------------------------------------
    406 static jint android_media_AudioRecord_readInShortArray(JNIEnv *env,  jobject thiz,
    407                                                         jshortArray javaAudioData,
    408                                                         jint offsetInShorts, jint sizeInShorts) {
    409 
    410     return (android_media_AudioRecord_readInByteArray(env, thiz,
    411                                                         (jbyteArray) javaAudioData,
    412                                                         offsetInShorts*2, sizeInShorts*2)
    413             / 2);
    414 }
    415 
    416 // ----------------------------------------------------------------------------
    417 static jint android_media_AudioRecord_readInDirectBuffer(JNIEnv *env,  jobject thiz,
    418                                                   jobject jBuffer, jint sizeInBytes) {
    419     // get the audio recorder from which we'll read new audio samples
    420     sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
    421     if (lpRecorder==NULL)
    422         return 0;
    423 
    424     // direct buffer and direct access supported?
    425     long capacity = env->GetDirectBufferCapacity(jBuffer);
    426     if (capacity == -1) {
    427         // buffer direct access is not supported
    428         ALOGE("Buffer direct access is not supported, can't record");
    429         return 0;
    430     }
    431     //ALOGV("capacity = %ld", capacity);
    432     jbyte* nativeFromJavaBuf = (jbyte*) env->GetDirectBufferAddress(jBuffer);
    433     if (nativeFromJavaBuf==NULL) {
    434         ALOGE("Buffer direct access is not supported, can't record");
    435         return 0;
    436     }
    437 
    438     // read new data from the recorder
    439     return (jint) lpRecorder->read(nativeFromJavaBuf,
    440                                    capacity < sizeInBytes ? capacity : sizeInBytes);
    441 }
    442 
    443 
    444 // ----------------------------------------------------------------------------
    445 static jint android_media_AudioRecord_set_marker_pos(JNIEnv *env,  jobject thiz,
    446         jint markerPos) {
    447 
    448     sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
    449     if (lpRecorder == NULL) {
    450         jniThrowException(env, "java/lang/IllegalStateException",
    451             "Unable to retrieve AudioRecord pointer for setMarkerPosition()");
    452         return AUDIORECORD_ERROR;
    453     }
    454     return android_media_translateRecorderErrorCode( lpRecorder->setMarkerPosition(markerPos) );
    455 }
    456 
    457 
    458 // ----------------------------------------------------------------------------
    459 static jint android_media_AudioRecord_get_marker_pos(JNIEnv *env,  jobject thiz) {
    460 
    461     sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
    462     uint32_t markerPos = 0;
    463 
    464     if (lpRecorder == NULL) {
    465         jniThrowException(env, "java/lang/IllegalStateException",
    466             "Unable to retrieve AudioRecord pointer for getMarkerPosition()");
    467         return AUDIORECORD_ERROR;
    468     }
    469     lpRecorder->getMarkerPosition(&markerPos);
    470     return (jint)markerPos;
    471 }
    472 
    473 
    474 // ----------------------------------------------------------------------------
    475 static jint android_media_AudioRecord_set_pos_update_period(JNIEnv *env,  jobject thiz,
    476         jint period) {
    477 
    478     sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
    479 
    480     if (lpRecorder == NULL) {
    481         jniThrowException(env, "java/lang/IllegalStateException",
    482             "Unable to retrieve AudioRecord pointer for setPositionUpdatePeriod()");
    483         return AUDIORECORD_ERROR;
    484     }
    485     return android_media_translateRecorderErrorCode( lpRecorder->setPositionUpdatePeriod(period) );
    486 }
    487 
    488 
    489 // ----------------------------------------------------------------------------
    490 static jint android_media_AudioRecord_get_pos_update_period(JNIEnv *env,  jobject thiz) {
    491 
    492     sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
    493     uint32_t period = 0;
    494 
    495     if (lpRecorder == NULL) {
    496         jniThrowException(env, "java/lang/IllegalStateException",
    497             "Unable to retrieve AudioRecord pointer for getPositionUpdatePeriod()");
    498         return AUDIORECORD_ERROR;
    499     }
    500     lpRecorder->getPositionUpdatePeriod(&period);
    501     return (jint)period;
    502 }
    503 
    504 
    505 // ----------------------------------------------------------------------------
    506 // returns the minimum required size for the successful creation of an AudioRecord instance.
    507 // returns 0 if the parameter combination is not supported.
    508 // return -1 if there was an error querying the buffer size.
    509 static jint android_media_AudioRecord_get_min_buff_size(JNIEnv *env,  jobject thiz,
    510     jint sampleRateInHertz, jint nbChannels, jint audioFormat) {
    511 
    512     ALOGV(">> android_media_AudioRecord_get_min_buff_size(%d, %d, %d)",
    513           sampleRateInHertz, nbChannels, audioFormat);
    514 
    515     int frameCount = 0;
    516     status_t result = AudioRecord::getMinFrameCount(&frameCount,
    517             sampleRateInHertz,
    518             (audioFormat == javaAudioRecordFields.PCM16 ?
    519                 AUDIO_FORMAT_PCM_16_BIT : AUDIO_FORMAT_PCM_8_BIT),
    520             nbChannels);
    521 
    522     if (result == BAD_VALUE) {
    523         return 0;
    524     }
    525     if (result != NO_ERROR) {
    526         return -1;
    527     }
    528     return frameCount * nbChannels * (audioFormat == javaAudioRecordFields.PCM16 ? 2 : 1);
    529 }
    530 
    531 
    532 // ----------------------------------------------------------------------------
    533 // ----------------------------------------------------------------------------
    534 static JNINativeMethod gMethods[] = {
    535     // name,               signature,  funcPtr
    536     {"native_start",         "(II)I",    (void *)android_media_AudioRecord_start},
    537     {"native_stop",          "()V",    (void *)android_media_AudioRecord_stop},
    538     {"native_setup",         "(Ljava/lang/Object;IIIII[I)I",
    539                                        (void *)android_media_AudioRecord_setup},
    540     {"native_finalize",      "()V",    (void *)android_media_AudioRecord_finalize},
    541     {"native_release",       "()V",    (void *)android_media_AudioRecord_release},
    542     {"native_read_in_byte_array",
    543                              "([BII)I", (void *)android_media_AudioRecord_readInByteArray},
    544     {"native_read_in_short_array",
    545                              "([SII)I", (void *)android_media_AudioRecord_readInShortArray},
    546     {"native_read_in_direct_buffer","(Ljava/lang/Object;I)I",
    547                                        (void *)android_media_AudioRecord_readInDirectBuffer},
    548     {"native_set_marker_pos","(I)I",   (void *)android_media_AudioRecord_set_marker_pos},
    549     {"native_get_marker_pos","()I",    (void *)android_media_AudioRecord_get_marker_pos},
    550     {"native_set_pos_update_period",
    551                              "(I)I",   (void *)android_media_AudioRecord_set_pos_update_period},
    552     {"native_get_pos_update_period",
    553                              "()I",    (void *)android_media_AudioRecord_get_pos_update_period},
    554     {"native_get_min_buff_size",
    555                              "(III)I",   (void *)android_media_AudioRecord_get_min_buff_size},
    556 };
    557 
    558 // field names found in android/media/AudioRecord.java
    559 #define JAVA_POSTEVENT_CALLBACK_NAME  "postEventFromNative"
    560 #define JAVA_CONST_PCM16_NAME         "ENCODING_PCM_16BIT"
    561 #define JAVA_CONST_PCM8_NAME          "ENCODING_PCM_8BIT"
    562 #define JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME  "mNativeRecorderInJavaObj"
    563 #define JAVA_NATIVECALLBACKINFO_FIELD_NAME       "mNativeCallbackCookie"
    564 
    565 #define JAVA_AUDIOFORMAT_CLASS_NAME "android/media/AudioFormat"
    566 
    567 // ----------------------------------------------------------------------------
    568 
    569 extern bool android_media_getIntConstantFromClass(JNIEnv* pEnv,
    570                 jclass theClass, const char* className, const char* constName, int* constVal);
    571 
    572 // ----------------------------------------------------------------------------
    573 int register_android_media_AudioRecord(JNIEnv *env)
    574 {
    575     javaAudioRecordFields.postNativeEventInJava = NULL;
    576     javaAudioRecordFields.nativeRecorderInJavaObj = NULL;
    577     javaAudioRecordFields.nativeCallbackCookie = NULL;
    578 
    579 
    580     // Get the AudioRecord class
    581     jclass audioRecordClass = env->FindClass(kClassPathName);
    582     if (audioRecordClass == NULL) {
    583         ALOGE("Can't find %s", kClassPathName);
    584         return -1;
    585     }
    586     // Get the postEvent method
    587     javaAudioRecordFields.postNativeEventInJava = env->GetStaticMethodID(
    588             audioRecordClass,
    589             JAVA_POSTEVENT_CALLBACK_NAME, "(Ljava/lang/Object;IIILjava/lang/Object;)V");
    590     if (javaAudioRecordFields.postNativeEventInJava == NULL) {
    591         ALOGE("Can't find AudioRecord.%s", JAVA_POSTEVENT_CALLBACK_NAME);
    592         return -1;
    593     }
    594 
    595     // Get the variables
    596     //    mNativeRecorderInJavaObj
    597     javaAudioRecordFields.nativeRecorderInJavaObj =
    598         env->GetFieldID(audioRecordClass,
    599                         JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME, "I");
    600     if (javaAudioRecordFields.nativeRecorderInJavaObj == NULL) {
    601         ALOGE("Can't find AudioRecord.%s", JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME);
    602         return -1;
    603     }
    604     //     mNativeCallbackCookie
    605     javaAudioRecordFields.nativeCallbackCookie = env->GetFieldID(
    606             audioRecordClass,
    607             JAVA_NATIVECALLBACKINFO_FIELD_NAME, "I");
    608     if (javaAudioRecordFields.nativeCallbackCookie == NULL) {
    609         ALOGE("Can't find AudioRecord.%s", JAVA_NATIVECALLBACKINFO_FIELD_NAME);
    610         return -1;
    611     }
    612 
    613     // Get the format constants from the AudioFormat class
    614     jclass audioFormatClass = NULL;
    615     audioFormatClass = env->FindClass(JAVA_AUDIOFORMAT_CLASS_NAME);
    616     if (audioFormatClass == NULL) {
    617         ALOGE("Can't find %s", JAVA_AUDIOFORMAT_CLASS_NAME);
    618         return -1;
    619     }
    620     if ( !android_media_getIntConstantFromClass(env, audioFormatClass,
    621                 JAVA_AUDIOFORMAT_CLASS_NAME,
    622                 JAVA_CONST_PCM16_NAME, &(javaAudioRecordFields.PCM16))
    623            || !android_media_getIntConstantFromClass(env, audioFormatClass,
    624                 JAVA_AUDIOFORMAT_CLASS_NAME,
    625                 JAVA_CONST_PCM8_NAME, &(javaAudioRecordFields.PCM8)) ) {
    626         // error log performed in getIntConstantFromClass()
    627         return -1;
    628     }
    629 
    630     return AndroidRuntime::registerNativeMethods(env,
    631             kClassPathName, gMethods, NELEM(gMethods));
    632 }
    633 
    634 // ----------------------------------------------------------------------------
    635