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