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