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