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 
    244     if (status != NO_ERROR) {
    245         ALOGE("Error creating AudioRecord instance: initialization check failed with status %d.",
    246                 status);
    247         goto native_init_failure;
    248     }
    249 
    250     nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
    251     if (nSession == NULL) {
    252         ALOGE("Error creating AudioRecord: Error retrieving session id pointer");
    253         goto native_init_failure;
    254     }
    255     // read the audio session ID back from AudioRecord in case a new session was created during set()
    256     nSession[0] = lpRecorder->getSessionId();
    257     env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
    258     nSession = NULL;
    259 
    260     {   // scope for the lock
    261         Mutex::Autolock l(sLock);
    262         sAudioRecordCallBackCookies.add(lpCallbackData);
    263     }
    264     // save our newly created C++ AudioRecord in the "nativeRecorderInJavaObj" field
    265     // of the Java object
    266     setAudioRecord(env, thiz, lpRecorder);
    267 
    268     // save our newly created callback information in the "nativeCallbackCookie" field
    269     // of the Java object (in mNativeCallbackCookie) so we can free the memory in finalize()
    270     env->SetLongField(thiz, javaAudioRecordFields.nativeCallbackCookie, (jlong)lpCallbackData);
    271 
    272     return (jint) AUDIO_JAVA_SUCCESS;
    273 
    274     // failure:
    275 native_init_failure:
    276     env->DeleteGlobalRef(lpCallbackData->audioRecord_class);
    277     env->DeleteGlobalRef(lpCallbackData->audioRecord_ref);
    278     delete lpCallbackData;
    279     env->SetLongField(thiz, javaAudioRecordFields.nativeCallbackCookie, 0);
    280 
    281     return (jint) AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED;
    282 }
    283 
    284 
    285 
    286 // ----------------------------------------------------------------------------
    287 static jint
    288 android_media_AudioRecord_start(JNIEnv *env, jobject thiz, jint event, jint triggerSession)
    289 {
    290     sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
    291     if (lpRecorder == NULL ) {
    292         jniThrowException(env, "java/lang/IllegalStateException", NULL);
    293         return (jint) AUDIO_JAVA_ERROR;
    294     }
    295 
    296     return nativeToJavaStatus(
    297             lpRecorder->start((AudioSystem::sync_event_t)event, triggerSession));
    298 }
    299 
    300 
    301 // ----------------------------------------------------------------------------
    302 static void
    303 android_media_AudioRecord_stop(JNIEnv *env, jobject thiz)
    304 {
    305     sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
    306     if (lpRecorder == NULL ) {
    307         jniThrowException(env, "java/lang/IllegalStateException", NULL);
    308         return;
    309     }
    310 
    311     lpRecorder->stop();
    312     //ALOGV("Called lpRecorder->stop()");
    313 }
    314 
    315 
    316 // ----------------------------------------------------------------------------
    317 
    318 #define CALLBACK_COND_WAIT_TIMEOUT_MS 1000
    319 static void android_media_AudioRecord_release(JNIEnv *env,  jobject thiz) {
    320     sp<AudioRecord> lpRecorder = setAudioRecord(env, thiz, 0);
    321     if (lpRecorder == NULL) {
    322         return;
    323     }
    324     ALOGV("About to delete lpRecorder: %p", lpRecorder.get());
    325     lpRecorder->stop();
    326 
    327     audiorecord_callback_cookie *lpCookie = (audiorecord_callback_cookie *)env->GetLongField(
    328         thiz, javaAudioRecordFields.nativeCallbackCookie);
    329 
    330     // reset the native resources in the Java object so any attempt to access
    331     // them after a call to release fails.
    332     env->SetLongField(thiz, javaAudioRecordFields.nativeCallbackCookie, 0);
    333 
    334     // delete the callback information
    335     if (lpCookie) {
    336         Mutex::Autolock l(sLock);
    337         ALOGV("deleting lpCookie: %p", lpCookie);
    338         while (lpCookie->busy) {
    339             if (lpCookie->cond.waitRelative(sLock,
    340                                             milliseconds(CALLBACK_COND_WAIT_TIMEOUT_MS)) !=
    341                                                     NO_ERROR) {
    342                 break;
    343             }
    344         }
    345         sAudioRecordCallBackCookies.remove(lpCookie);
    346         env->DeleteGlobalRef(lpCookie->audioRecord_class);
    347         env->DeleteGlobalRef(lpCookie->audioRecord_ref);
    348         delete lpCookie;
    349     }
    350 }
    351 
    352 
    353 // ----------------------------------------------------------------------------
    354 static void android_media_AudioRecord_finalize(JNIEnv *env,  jobject thiz) {
    355     android_media_AudioRecord_release(env, thiz);
    356 }
    357 
    358 
    359 // ----------------------------------------------------------------------------
    360 static jint android_media_AudioRecord_readInByteArray(JNIEnv *env,  jobject thiz,
    361                                                         jbyteArray javaAudioData,
    362                                                         jint offsetInBytes, jint sizeInBytes) {
    363     jbyte* recordBuff = NULL;
    364     // get the audio recorder from which we'll read new audio samples
    365     sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
    366     if (lpRecorder == NULL) {
    367         ALOGE("Unable to retrieve AudioRecord object, can't record");
    368         return 0;
    369     }
    370 
    371     if (!javaAudioData) {
    372         ALOGE("Invalid Java array to store recorded audio, can't record");
    373         return 0;
    374     }
    375 
    376     // get the pointer to where we'll record the audio
    377     // NOTE: We may use GetPrimitiveArrayCritical() when the JNI implementation changes in such
    378     // a way that it becomes much more efficient. When doing so, we will have to prevent the
    379     // AudioSystem callback to be called while in critical section (in case of media server
    380     // process crash for instance)
    381     recordBuff = (jbyte *)env->GetByteArrayElements(javaAudioData, NULL);
    382 
    383     if (recordBuff == NULL) {
    384         ALOGE("Error retrieving destination for recorded audio data, can't record");
    385         return 0;
    386     }
    387 
    388     // read the new audio data from the native AudioRecord object
    389     ssize_t recorderBuffSize = lpRecorder->frameCount()*lpRecorder->frameSize();
    390     ssize_t readSize = lpRecorder->read(recordBuff + offsetInBytes,
    391                                         sizeInBytes > (jint)recorderBuffSize ?
    392                                             (jint)recorderBuffSize : sizeInBytes );
    393     env->ReleaseByteArrayElements(javaAudioData, recordBuff, 0);
    394 
    395     if (readSize < 0) {
    396         readSize = (jint)AUDIO_JAVA_INVALID_OPERATION;
    397     }
    398     return (jint) readSize;
    399 }
    400 
    401 // ----------------------------------------------------------------------------
    402 static jint android_media_AudioRecord_readInShortArray(JNIEnv *env,  jobject thiz,
    403                                                         jshortArray javaAudioData,
    404                                                         jint offsetInShorts, jint sizeInShorts) {
    405 
    406     jshort* recordBuff = NULL;
    407     // get the audio recorder from which we'll read new audio samples
    408     sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
    409     if (lpRecorder == NULL) {
    410         ALOGE("Unable to retrieve AudioRecord object, can't record");
    411         return 0;
    412     }
    413 
    414     if (!javaAudioData) {
    415         ALOGE("Invalid Java array to store recorded audio, can't record");
    416         return 0;
    417     }
    418 
    419     // get the pointer to where we'll record the audio
    420     // NOTE: We may use GetPrimitiveArrayCritical() when the JNI implementation changes in such
    421     // a way that it becomes much more efficient. When doing so, we will have to prevent the
    422     // AudioSystem callback to be called while in critical section (in case of media server
    423     // process crash for instance)
    424     recordBuff = (jshort *)env->GetShortArrayElements(javaAudioData, NULL);
    425 
    426     if (recordBuff == NULL) {
    427         ALOGE("Error retrieving destination for recorded audio data, can't record");
    428         return 0;
    429     }
    430 
    431     // read the new audio data from the native AudioRecord object
    432     const size_t recorderBuffSize = lpRecorder->frameCount()*lpRecorder->frameSize();
    433     const size_t sizeInBytes = sizeInShorts * sizeof(short);
    434     ssize_t readSize = lpRecorder->read(recordBuff + offsetInShorts * sizeof(short),
    435                                         sizeInBytes > recorderBuffSize ?
    436                                             recorderBuffSize : sizeInBytes);
    437 
    438     env->ReleaseShortArrayElements(javaAudioData, recordBuff, 0);
    439 
    440     if (readSize < 0) {
    441         readSize = (jint)AUDIO_JAVA_INVALID_OPERATION;
    442     } else {
    443         readSize /= sizeof(short);
    444     }
    445     return (jint) readSize;
    446 }
    447 
    448 // ----------------------------------------------------------------------------
    449 static jint android_media_AudioRecord_readInDirectBuffer(JNIEnv *env,  jobject thiz,
    450                                                   jobject jBuffer, jint sizeInBytes) {
    451     // get the audio recorder from which we'll read new audio samples
    452     sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
    453     if (lpRecorder==NULL)
    454         return 0;
    455 
    456     // direct buffer and direct access supported?
    457     long capacity = env->GetDirectBufferCapacity(jBuffer);
    458     if (capacity == -1) {
    459         // buffer direct access is not supported
    460         ALOGE("Buffer direct access is not supported, can't record");
    461         return 0;
    462     }
    463     //ALOGV("capacity = %ld", capacity);
    464     jbyte* nativeFromJavaBuf = (jbyte*) env->GetDirectBufferAddress(jBuffer);
    465     if (nativeFromJavaBuf==NULL) {
    466         ALOGE("Buffer direct access is not supported, can't record");
    467         return 0;
    468     }
    469 
    470     // read new data from the recorder
    471     ssize_t readSize = lpRecorder->read(nativeFromJavaBuf,
    472                                    capacity < sizeInBytes ? capacity : sizeInBytes);
    473     if (readSize < 0) {
    474         readSize = (jint)AUDIO_JAVA_INVALID_OPERATION;
    475     }
    476     return (jint)readSize;
    477 }
    478 
    479 
    480 // ----------------------------------------------------------------------------
    481 static jint android_media_AudioRecord_set_marker_pos(JNIEnv *env,  jobject thiz,
    482         jint markerPos) {
    483 
    484     sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
    485     if (lpRecorder == NULL) {
    486         jniThrowException(env, "java/lang/IllegalStateException",
    487             "Unable to retrieve AudioRecord pointer for setMarkerPosition()");
    488         return (jint)AUDIO_JAVA_ERROR;
    489     }
    490     return nativeToJavaStatus( lpRecorder->setMarkerPosition(markerPos) );
    491 }
    492 
    493 
    494 // ----------------------------------------------------------------------------
    495 static jint android_media_AudioRecord_get_marker_pos(JNIEnv *env,  jobject thiz) {
    496 
    497     sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
    498     uint32_t markerPos = 0;
    499 
    500     if (lpRecorder == NULL) {
    501         jniThrowException(env, "java/lang/IllegalStateException",
    502             "Unable to retrieve AudioRecord pointer for getMarkerPosition()");
    503         return (jint)AUDIO_JAVA_ERROR;
    504     }
    505     lpRecorder->getMarkerPosition(&markerPos);
    506     return (jint)markerPos;
    507 }
    508 
    509 
    510 // ----------------------------------------------------------------------------
    511 static jint android_media_AudioRecord_set_pos_update_period(JNIEnv *env,  jobject thiz,
    512         jint period) {
    513 
    514     sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
    515 
    516     if (lpRecorder == NULL) {
    517         jniThrowException(env, "java/lang/IllegalStateException",
    518             "Unable to retrieve AudioRecord pointer for setPositionUpdatePeriod()");
    519         return (jint)AUDIO_JAVA_ERROR;
    520     }
    521     return nativeToJavaStatus( lpRecorder->setPositionUpdatePeriod(period) );
    522 }
    523 
    524 
    525 // ----------------------------------------------------------------------------
    526 static jint android_media_AudioRecord_get_pos_update_period(JNIEnv *env,  jobject thiz) {
    527 
    528     sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
    529     uint32_t period = 0;
    530 
    531     if (lpRecorder == NULL) {
    532         jniThrowException(env, "java/lang/IllegalStateException",
    533             "Unable to retrieve AudioRecord pointer for getPositionUpdatePeriod()");
    534         return (jint)AUDIO_JAVA_ERROR;
    535     }
    536     lpRecorder->getPositionUpdatePeriod(&period);
    537     return (jint)period;
    538 }
    539 
    540 
    541 // ----------------------------------------------------------------------------
    542 // returns the minimum required size for the successful creation of an AudioRecord instance.
    543 // returns 0 if the parameter combination is not supported.
    544 // return -1 if there was an error querying the buffer size.
    545 static jint android_media_AudioRecord_get_min_buff_size(JNIEnv *env,  jobject thiz,
    546     jint sampleRateInHertz, jint channelCount, jint audioFormat) {
    547 
    548     ALOGV(">> android_media_AudioRecord_get_min_buff_size(%d, %d, %d)",
    549           sampleRateInHertz, channelCount, audioFormat);
    550 
    551     size_t frameCount = 0;
    552     audio_format_t format = audioFormatToNative(audioFormat);
    553     status_t result = AudioRecord::getMinFrameCount(&frameCount,
    554             sampleRateInHertz,
    555             format,
    556             audio_channel_in_mask_from_count(channelCount));
    557 
    558     if (result == BAD_VALUE) {
    559         return 0;
    560     }
    561     if (result != NO_ERROR) {
    562         return -1;
    563     }
    564     return frameCount * channelCount * audio_bytes_per_sample(format);
    565 }
    566 
    567 
    568 // ----------------------------------------------------------------------------
    569 // ----------------------------------------------------------------------------
    570 static JNINativeMethod gMethods[] = {
    571     // name,               signature,  funcPtr
    572     {"native_start",         "(II)I",    (void *)android_media_AudioRecord_start},
    573     {"native_stop",          "()V",    (void *)android_media_AudioRecord_stop},
    574     {"native_setup",         "(Ljava/lang/Object;Ljava/lang/Object;IIII[I)I",
    575                                        (void *)android_media_AudioRecord_setup},
    576     {"native_finalize",      "()V",    (void *)android_media_AudioRecord_finalize},
    577     {"native_release",       "()V",    (void *)android_media_AudioRecord_release},
    578     {"native_read_in_byte_array",
    579                              "([BII)I", (void *)android_media_AudioRecord_readInByteArray},
    580     {"native_read_in_short_array",
    581                              "([SII)I", (void *)android_media_AudioRecord_readInShortArray},
    582     {"native_read_in_direct_buffer","(Ljava/lang/Object;I)I",
    583                                        (void *)android_media_AudioRecord_readInDirectBuffer},
    584     {"native_set_marker_pos","(I)I",   (void *)android_media_AudioRecord_set_marker_pos},
    585     {"native_get_marker_pos","()I",    (void *)android_media_AudioRecord_get_marker_pos},
    586     {"native_set_pos_update_period",
    587                              "(I)I",   (void *)android_media_AudioRecord_set_pos_update_period},
    588     {"native_get_pos_update_period",
    589                              "()I",    (void *)android_media_AudioRecord_get_pos_update_period},
    590     {"native_get_min_buff_size",
    591                              "(III)I",   (void *)android_media_AudioRecord_get_min_buff_size},
    592 };
    593 
    594 // field names found in android/media/AudioRecord.java
    595 #define JAVA_POSTEVENT_CALLBACK_NAME  "postEventFromNative"
    596 #define JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME  "mNativeRecorderInJavaObj"
    597 #define JAVA_NATIVECALLBACKINFO_FIELD_NAME       "mNativeCallbackCookie"
    598 
    599 // ----------------------------------------------------------------------------
    600 int register_android_media_AudioRecord(JNIEnv *env)
    601 {
    602     javaAudioRecordFields.postNativeEventInJava = NULL;
    603     javaAudioRecordFields.nativeRecorderInJavaObj = NULL;
    604     javaAudioRecordFields.nativeCallbackCookie = NULL;
    605 
    606 
    607     // Get the AudioRecord class
    608     jclass audioRecordClass = env->FindClass(kClassPathName);
    609     if (audioRecordClass == NULL) {
    610         ALOGE("Can't find %s", kClassPathName);
    611         return -1;
    612     }
    613     // Get the postEvent method
    614     javaAudioRecordFields.postNativeEventInJava = env->GetStaticMethodID(
    615             audioRecordClass,
    616             JAVA_POSTEVENT_CALLBACK_NAME, "(Ljava/lang/Object;IIILjava/lang/Object;)V");
    617     if (javaAudioRecordFields.postNativeEventInJava == NULL) {
    618         ALOGE("Can't find AudioRecord.%s", JAVA_POSTEVENT_CALLBACK_NAME);
    619         return -1;
    620     }
    621 
    622     // Get the variables
    623     //    mNativeRecorderInJavaObj
    624     javaAudioRecordFields.nativeRecorderInJavaObj =
    625         env->GetFieldID(audioRecordClass,
    626                         JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME, "J");
    627     if (javaAudioRecordFields.nativeRecorderInJavaObj == NULL) {
    628         ALOGE("Can't find AudioRecord.%s", JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME);
    629         return -1;
    630     }
    631     //     mNativeCallbackCookie
    632     javaAudioRecordFields.nativeCallbackCookie = env->GetFieldID(
    633             audioRecordClass,
    634             JAVA_NATIVECALLBACKINFO_FIELD_NAME, "J");
    635     if (javaAudioRecordFields.nativeCallbackCookie == NULL) {
    636         ALOGE("Can't find AudioRecord.%s", JAVA_NATIVECALLBACKINFO_FIELD_NAME);
    637         return -1;
    638     }
    639 
    640     // Get the AudioAttributes class and fields
    641     jclass audioAttrClass = env->FindClass(kAudioAttributesClassPathName);
    642     if (audioAttrClass == NULL) {
    643         ALOGE("Can't find %s", kAudioAttributesClassPathName);
    644         return -1;
    645     }
    646     jclass audioAttributesClassRef = (jclass)env->NewGlobalRef(audioAttrClass);
    647     javaAudioAttrFields.fieldRecSource = env->GetFieldID(audioAttributesClassRef, "mSource", "I");
    648     javaAudioAttrFields.fieldFlags = env->GetFieldID(audioAttributesClassRef, "mFlags", "I");
    649     javaAudioAttrFields.fieldFormattedTags =
    650             env->GetFieldID(audioAttributesClassRef, "mFormattedTags", "Ljava/lang/String;");
    651     env->DeleteGlobalRef(audioAttributesClassRef);
    652     if (javaAudioAttrFields.fieldRecSource == NULL
    653             || javaAudioAttrFields.fieldFlags == NULL
    654             || javaAudioAttrFields.fieldFormattedTags == NULL) {
    655         ALOGE("Can't initialize AudioAttributes fields");
    656         return -1;
    657     }
    658 
    659     return AndroidRuntime::registerNativeMethods(env,
    660             kClassPathName, gMethods, NELEM(gMethods));
    661 }
    662 
    663 // ----------------------------------------------------------------------------
    664