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