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
42using namespace android;
43
44// ----------------------------------------------------------------------------
45static const char* const kClassPathName = "android/media/AudioRecord";
46
47struct 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};
55static fields_t javaAudioRecordFields;
56
57struct audiorecord_callback_cookie {
58    jclass      audioRecord_class;
59    jobject     audioRecord_ref;
60    bool        busy;
61    Condition   cond;
62};
63
64static Mutex sLock;
65static 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
79jint 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// ----------------------------------------------------------------------------
94static 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// ----------------------------------------------------------------------------
143static 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
151static 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(thiz);
158    }
159    if (old != 0) {
160        old->decStrong(thiz);
161    }
162    env->SetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj, (int)ar.get());
163    return old;
164}
165
166// ----------------------------------------------------------------------------
167static int
168android_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    if (lpRecorder == NULL) {
228        ALOGE("Error creating AudioRecord instance.");
229        return AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED;
230    }
231
232    // create the callback information:
233    // this data will be passed with every AudioRecord callback
234    audiorecord_callback_cookie *lpCallbackData = new audiorecord_callback_cookie;
235    lpCallbackData->audioRecord_class = (jclass)env->NewGlobalRef(clazz);
236    // we use a weak reference so the AudioRecord object can be garbage collected.
237    lpCallbackData->audioRecord_ref = env->NewGlobalRef(weak_this);
238    lpCallbackData->busy = false;
239
240    lpRecorder->set((audio_source_t) source,
241        sampleRateInHertz,
242        format,        // word length, PCM
243        channels,
244        frameCount,
245        (AudioRecord::record_flags) 0,  // flags
246        recorderCallback,// callback_t
247        lpCallbackData,// void* user
248        0,             // notificationFrames,
249        true,          // threadCanCallJava)
250        sessionId);
251
252    if (lpRecorder->initCheck() != NO_ERROR) {
253        ALOGE("Error creating AudioRecord instance: initialization check failed.");
254        goto native_init_failure;
255    }
256
257    nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
258    if (nSession == NULL) {
259        ALOGE("Error creating AudioRecord: Error retrieving session id pointer");
260        goto native_init_failure;
261    }
262    // read the audio session ID back from AudioRecord in case a new session was created during set()
263    nSession[0] = lpRecorder->getSessionId();
264    env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
265    nSession = NULL;
266
267    {   // scope for the lock
268        Mutex::Autolock l(sLock);
269        sAudioRecordCallBackCookies.add(lpCallbackData);
270    }
271    // save our newly created C++ AudioRecord in the "nativeRecorderInJavaObj" field
272    // of the Java object
273    setAudioRecord(env, thiz, lpRecorder);
274
275    // save our newly created callback information in the "nativeCallbackCookie" field
276    // of the Java object (in mNativeCallbackCookie) so we can free the memory in finalize()
277    env->SetIntField(thiz, javaAudioRecordFields.nativeCallbackCookie, (int)lpCallbackData);
278
279    return AUDIORECORD_SUCCESS;
280
281    // failure:
282native_init_failure:
283    env->DeleteGlobalRef(lpCallbackData->audioRecord_class);
284    env->DeleteGlobalRef(lpCallbackData->audioRecord_ref);
285    delete lpCallbackData;
286    env->SetIntField(thiz, javaAudioRecordFields.nativeCallbackCookie, 0);
287
288    return AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED;
289}
290
291
292
293// ----------------------------------------------------------------------------
294static int
295android_media_AudioRecord_start(JNIEnv *env, jobject thiz, jint event, jint triggerSession)
296{
297    sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
298    if (lpRecorder == NULL ) {
299        jniThrowException(env, "java/lang/IllegalStateException", NULL);
300        return AUDIORECORD_ERROR;
301    }
302
303    return android_media_translateRecorderErrorCode(
304            lpRecorder->start((AudioSystem::sync_event_t)event, triggerSession));
305}
306
307
308// ----------------------------------------------------------------------------
309static void
310android_media_AudioRecord_stop(JNIEnv *env, jobject thiz)
311{
312    sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
313    if (lpRecorder == NULL ) {
314        jniThrowException(env, "java/lang/IllegalStateException", NULL);
315        return;
316    }
317
318    lpRecorder->stop();
319    //ALOGV("Called lpRecorder->stop()");
320}
321
322
323// ----------------------------------------------------------------------------
324
325#define CALLBACK_COND_WAIT_TIMEOUT_MS 1000
326static void android_media_AudioRecord_release(JNIEnv *env,  jobject thiz) {
327    sp<AudioRecord> lpRecorder = setAudioRecord(env, thiz, 0);
328    if (lpRecorder == NULL) {
329        return;
330    }
331    ALOGV("About to delete lpRecorder: %x\n", (int)lpRecorder.get());
332    lpRecorder->stop();
333
334    audiorecord_callback_cookie *lpCookie = (audiorecord_callback_cookie *)env->GetIntField(
335        thiz, javaAudioRecordFields.nativeCallbackCookie);
336
337    // reset the native resources in the Java object so any attempt to access
338    // them after a call to release fails.
339    env->SetIntField(thiz, javaAudioRecordFields.nativeCallbackCookie, 0);
340
341    // delete the callback information
342    if (lpCookie) {
343        Mutex::Autolock l(sLock);
344        ALOGV("deleting lpCookie: %x\n", (int)lpCookie);
345        while (lpCookie->busy) {
346            if (lpCookie->cond.waitRelative(sLock,
347                                            milliseconds(CALLBACK_COND_WAIT_TIMEOUT_MS)) !=
348                                                    NO_ERROR) {
349                break;
350            }
351        }
352        sAudioRecordCallBackCookies.remove(lpCookie);
353        env->DeleteGlobalRef(lpCookie->audioRecord_class);
354        env->DeleteGlobalRef(lpCookie->audioRecord_ref);
355        delete lpCookie;
356    }
357}
358
359
360// ----------------------------------------------------------------------------
361static void android_media_AudioRecord_finalize(JNIEnv *env,  jobject thiz) {
362    android_media_AudioRecord_release(env, thiz);
363}
364
365
366// ----------------------------------------------------------------------------
367static jint android_media_AudioRecord_readInByteArray(JNIEnv *env,  jobject thiz,
368                                                        jbyteArray javaAudioData,
369                                                        jint offsetInBytes, jint sizeInBytes) {
370    jbyte* recordBuff = NULL;
371    // get the audio recorder from which we'll read new audio samples
372    sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
373    if (lpRecorder == NULL) {
374        ALOGE("Unable to retrieve AudioRecord object, can't record");
375        return 0;
376    }
377
378    if (!javaAudioData) {
379        ALOGE("Invalid Java array to store recorded audio, can't record");
380        return 0;
381    }
382
383    // get the pointer to where we'll record the audio
384    // NOTE: We may use GetPrimitiveArrayCritical() when the JNI implementation changes in such
385    // a way that it becomes much more efficient. When doing so, we will have to prevent the
386    // AudioSystem callback to be called while in critical section (in case of media server
387    // process crash for instance)
388    recordBuff = (jbyte *)env->GetByteArrayElements(javaAudioData, NULL);
389
390    if (recordBuff == NULL) {
391        ALOGE("Error retrieving destination for recorded audio data, can't record");
392        return 0;
393    }
394
395    // read the new audio data from the native AudioRecord object
396    ssize_t recorderBuffSize = lpRecorder->frameCount()*lpRecorder->frameSize();
397    ssize_t readSize = lpRecorder->read(recordBuff + offsetInBytes,
398                                        sizeInBytes > (jint)recorderBuffSize ?
399                                            (jint)recorderBuffSize : sizeInBytes );
400    env->ReleaseByteArrayElements(javaAudioData, recordBuff, 0);
401
402    return (jint) readSize;
403}
404
405// ----------------------------------------------------------------------------
406static jint android_media_AudioRecord_readInShortArray(JNIEnv *env,  jobject thiz,
407                                                        jshortArray javaAudioData,
408                                                        jint offsetInShorts, jint sizeInShorts) {
409
410    return (android_media_AudioRecord_readInByteArray(env, thiz,
411                                                        (jbyteArray) javaAudioData,
412                                                        offsetInShorts*2, sizeInShorts*2)
413            / 2);
414}
415
416// ----------------------------------------------------------------------------
417static jint android_media_AudioRecord_readInDirectBuffer(JNIEnv *env,  jobject thiz,
418                                                  jobject jBuffer, jint sizeInBytes) {
419    // get the audio recorder from which we'll read new audio samples
420    sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
421    if (lpRecorder==NULL)
422        return 0;
423
424    // direct buffer and direct access supported?
425    long capacity = env->GetDirectBufferCapacity(jBuffer);
426    if (capacity == -1) {
427        // buffer direct access is not supported
428        ALOGE("Buffer direct access is not supported, can't record");
429        return 0;
430    }
431    //ALOGV("capacity = %ld", capacity);
432    jbyte* nativeFromJavaBuf = (jbyte*) env->GetDirectBufferAddress(jBuffer);
433    if (nativeFromJavaBuf==NULL) {
434        ALOGE("Buffer direct access is not supported, can't record");
435        return 0;
436    }
437
438    // read new data from the recorder
439    return (jint) lpRecorder->read(nativeFromJavaBuf,
440                                   capacity < sizeInBytes ? capacity : sizeInBytes);
441}
442
443
444// ----------------------------------------------------------------------------
445static jint android_media_AudioRecord_set_marker_pos(JNIEnv *env,  jobject thiz,
446        jint markerPos) {
447
448    sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
449    if (lpRecorder == NULL) {
450        jniThrowException(env, "java/lang/IllegalStateException",
451            "Unable to retrieve AudioRecord pointer for setMarkerPosition()");
452        return AUDIORECORD_ERROR;
453    }
454    return android_media_translateRecorderErrorCode( lpRecorder->setMarkerPosition(markerPos) );
455}
456
457
458// ----------------------------------------------------------------------------
459static jint android_media_AudioRecord_get_marker_pos(JNIEnv *env,  jobject thiz) {
460
461    sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
462    uint32_t markerPos = 0;
463
464    if (lpRecorder == NULL) {
465        jniThrowException(env, "java/lang/IllegalStateException",
466            "Unable to retrieve AudioRecord pointer for getMarkerPosition()");
467        return AUDIORECORD_ERROR;
468    }
469    lpRecorder->getMarkerPosition(&markerPos);
470    return (jint)markerPos;
471}
472
473
474// ----------------------------------------------------------------------------
475static jint android_media_AudioRecord_set_pos_update_period(JNIEnv *env,  jobject thiz,
476        jint period) {
477
478    sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
479
480    if (lpRecorder == NULL) {
481        jniThrowException(env, "java/lang/IllegalStateException",
482            "Unable to retrieve AudioRecord pointer for setPositionUpdatePeriod()");
483        return AUDIORECORD_ERROR;
484    }
485    return android_media_translateRecorderErrorCode( lpRecorder->setPositionUpdatePeriod(period) );
486}
487
488
489// ----------------------------------------------------------------------------
490static jint android_media_AudioRecord_get_pos_update_period(JNIEnv *env,  jobject thiz) {
491
492    sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
493    uint32_t period = 0;
494
495    if (lpRecorder == NULL) {
496        jniThrowException(env, "java/lang/IllegalStateException",
497            "Unable to retrieve AudioRecord pointer for getPositionUpdatePeriod()");
498        return AUDIORECORD_ERROR;
499    }
500    lpRecorder->getPositionUpdatePeriod(&period);
501    return (jint)period;
502}
503
504
505// ----------------------------------------------------------------------------
506// returns the minimum required size for the successful creation of an AudioRecord instance.
507// returns 0 if the parameter combination is not supported.
508// return -1 if there was an error querying the buffer size.
509static jint android_media_AudioRecord_get_min_buff_size(JNIEnv *env,  jobject thiz,
510    jint sampleRateInHertz, jint nbChannels, jint audioFormat) {
511
512    ALOGV(">> android_media_AudioRecord_get_min_buff_size(%d, %d, %d)",
513          sampleRateInHertz, nbChannels, audioFormat);
514
515    int frameCount = 0;
516    status_t result = AudioRecord::getMinFrameCount(&frameCount,
517            sampleRateInHertz,
518            (audioFormat == javaAudioRecordFields.PCM16 ?
519                AUDIO_FORMAT_PCM_16_BIT : AUDIO_FORMAT_PCM_8_BIT),
520            nbChannels);
521
522    if (result == BAD_VALUE) {
523        return 0;
524    }
525    if (result != NO_ERROR) {
526        return -1;
527    }
528    return frameCount * nbChannels * (audioFormat == javaAudioRecordFields.PCM16 ? 2 : 1);
529}
530
531
532// ----------------------------------------------------------------------------
533// ----------------------------------------------------------------------------
534static JNINativeMethod gMethods[] = {
535    // name,               signature,  funcPtr
536    {"native_start",         "(II)I",    (void *)android_media_AudioRecord_start},
537    {"native_stop",          "()V",    (void *)android_media_AudioRecord_stop},
538    {"native_setup",         "(Ljava/lang/Object;IIIII[I)I",
539                                       (void *)android_media_AudioRecord_setup},
540    {"native_finalize",      "()V",    (void *)android_media_AudioRecord_finalize},
541    {"native_release",       "()V",    (void *)android_media_AudioRecord_release},
542    {"native_read_in_byte_array",
543                             "([BII)I", (void *)android_media_AudioRecord_readInByteArray},
544    {"native_read_in_short_array",
545                             "([SII)I", (void *)android_media_AudioRecord_readInShortArray},
546    {"native_read_in_direct_buffer","(Ljava/lang/Object;I)I",
547                                       (void *)android_media_AudioRecord_readInDirectBuffer},
548    {"native_set_marker_pos","(I)I",   (void *)android_media_AudioRecord_set_marker_pos},
549    {"native_get_marker_pos","()I",    (void *)android_media_AudioRecord_get_marker_pos},
550    {"native_set_pos_update_period",
551                             "(I)I",   (void *)android_media_AudioRecord_set_pos_update_period},
552    {"native_get_pos_update_period",
553                             "()I",    (void *)android_media_AudioRecord_get_pos_update_period},
554    {"native_get_min_buff_size",
555                             "(III)I",   (void *)android_media_AudioRecord_get_min_buff_size},
556};
557
558// field names found in android/media/AudioRecord.java
559#define JAVA_POSTEVENT_CALLBACK_NAME  "postEventFromNative"
560#define JAVA_CONST_PCM16_NAME         "ENCODING_PCM_16BIT"
561#define JAVA_CONST_PCM8_NAME          "ENCODING_PCM_8BIT"
562#define JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME  "mNativeRecorderInJavaObj"
563#define JAVA_NATIVECALLBACKINFO_FIELD_NAME       "mNativeCallbackCookie"
564
565#define JAVA_AUDIOFORMAT_CLASS_NAME "android/media/AudioFormat"
566
567// ----------------------------------------------------------------------------
568
569extern bool android_media_getIntConstantFromClass(JNIEnv* pEnv,
570                jclass theClass, const char* className, const char* constName, int* constVal);
571
572// ----------------------------------------------------------------------------
573int register_android_media_AudioRecord(JNIEnv *env)
574{
575    javaAudioRecordFields.postNativeEventInJava = NULL;
576    javaAudioRecordFields.nativeRecorderInJavaObj = NULL;
577    javaAudioRecordFields.nativeCallbackCookie = NULL;
578
579
580    // Get the AudioRecord class
581    jclass audioRecordClass = env->FindClass(kClassPathName);
582    if (audioRecordClass == NULL) {
583        ALOGE("Can't find %s", kClassPathName);
584        return -1;
585    }
586    // Get the postEvent method
587    javaAudioRecordFields.postNativeEventInJava = env->GetStaticMethodID(
588            audioRecordClass,
589            JAVA_POSTEVENT_CALLBACK_NAME, "(Ljava/lang/Object;IIILjava/lang/Object;)V");
590    if (javaAudioRecordFields.postNativeEventInJava == NULL) {
591        ALOGE("Can't find AudioRecord.%s", JAVA_POSTEVENT_CALLBACK_NAME);
592        return -1;
593    }
594
595    // Get the variables
596    //    mNativeRecorderInJavaObj
597    javaAudioRecordFields.nativeRecorderInJavaObj =
598        env->GetFieldID(audioRecordClass,
599                        JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME, "I");
600    if (javaAudioRecordFields.nativeRecorderInJavaObj == NULL) {
601        ALOGE("Can't find AudioRecord.%s", JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME);
602        return -1;
603    }
604    //     mNativeCallbackCookie
605    javaAudioRecordFields.nativeCallbackCookie = env->GetFieldID(
606            audioRecordClass,
607            JAVA_NATIVECALLBACKINFO_FIELD_NAME, "I");
608    if (javaAudioRecordFields.nativeCallbackCookie == NULL) {
609        ALOGE("Can't find AudioRecord.%s", JAVA_NATIVECALLBACKINFO_FIELD_NAME);
610        return -1;
611    }
612
613    // Get the format constants from the AudioFormat class
614    jclass audioFormatClass = NULL;
615    audioFormatClass = env->FindClass(JAVA_AUDIOFORMAT_CLASS_NAME);
616    if (audioFormatClass == NULL) {
617        ALOGE("Can't find %s", JAVA_AUDIOFORMAT_CLASS_NAME);
618        return -1;
619    }
620    if ( !android_media_getIntConstantFromClass(env, audioFormatClass,
621                JAVA_AUDIOFORMAT_CLASS_NAME,
622                JAVA_CONST_PCM16_NAME, &(javaAudioRecordFields.PCM16))
623           || !android_media_getIntConstantFromClass(env, audioFormatClass,
624                JAVA_AUDIOFORMAT_CLASS_NAME,
625                JAVA_CONST_PCM8_NAME, &(javaAudioRecordFields.PCM8)) ) {
626        // error log performed in getIntConstantFromClass()
627        return -1;
628    }
629
630    return AndroidRuntime::registerNativeMethods(env,
631            kClassPathName, gMethods, NELEM(gMethods));
632}
633
634// ----------------------------------------------------------------------------
635