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