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