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