android_media_AudioRecord.cpp revision a5a42384367230b8e16cf441d5dcf4225332f9d7
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    jfieldID  nativeRecorderInJavaObj; // provides access to the C++ AudioRecord object
41    jfieldID  nativeCallbackCookie;    // provides access to the AudioRecord callback data
42};
43static fields_t javaAudioRecordFields;
44
45struct audiorecord_callback_cookie {
46    jclass      audioRecord_class;
47    jobject     audioRecord_ref;
48    bool        busy;
49    Condition   cond;
50};
51
52// keep these values in sync with AudioFormat.java
53#define ENCODING_PCM_16BIT 2
54#define ENCODING_PCM_8BIT  3
55
56static Mutex sLock;
57static SortedVector <audiorecord_callback_cookie *> sAudioRecordCallBackCookies;
58
59// ----------------------------------------------------------------------------
60
61#define AUDIORECORD_SUCCESS                         0
62#define AUDIORECORD_ERROR                           -1
63#define AUDIORECORD_ERROR_BAD_VALUE                 -2
64#define AUDIORECORD_ERROR_INVALID_OPERATION         -3
65#define AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT      -16
66#define AUDIORECORD_ERROR_SETUP_INVALIDCHANNELMASK -17
67#define AUDIORECORD_ERROR_SETUP_INVALIDFORMAT       -18
68#define AUDIORECORD_ERROR_SETUP_INVALIDSOURCE       -19
69#define AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED    -20
70
71jint android_media_translateRecorderErrorCode(int code) {
72    switch (code) {
73    case NO_ERROR:
74        return AUDIORECORD_SUCCESS;
75    case BAD_VALUE:
76        return AUDIORECORD_ERROR_BAD_VALUE;
77    case INVALID_OPERATION:
78        return AUDIORECORD_ERROR_INVALID_OPERATION;
79    default:
80        return AUDIORECORD_ERROR;
81    }
82}
83
84static audio_format_t audioFormatToNative(int audioFormat)
85{
86    switch (audioFormat) {
87    case ENCODING_PCM_16BIT:
88        return AUDIO_FORMAT_PCM_16_BIT;
89    case ENCODING_PCM_8BIT:
90        return AUDIO_FORMAT_PCM_8_BIT;
91    default:
92        return AUDIO_FORMAT_INVALID;
93    }
94}
95
96// ----------------------------------------------------------------------------
97static void recorderCallback(int event, void* user, void *info) {
98
99    audiorecord_callback_cookie *callbackInfo = (audiorecord_callback_cookie *)user;
100    {
101        Mutex::Autolock l(sLock);
102        if (sAudioRecordCallBackCookies.indexOf(callbackInfo) < 0) {
103            return;
104        }
105        callbackInfo->busy = true;
106    }
107
108    switch (event) {
109    case AudioRecord::EVENT_MARKER: {
110        JNIEnv *env = AndroidRuntime::getJNIEnv();
111        if (user != NULL && env != NULL) {
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        } break;
122
123    case AudioRecord::EVENT_NEW_POS: {
124        JNIEnv *env = AndroidRuntime::getJNIEnv();
125        if (user != NULL && env != NULL) {
126            env->CallStaticVoidMethod(
127                callbackInfo->audioRecord_class,
128                javaAudioRecordFields.postNativeEventInJava,
129                callbackInfo->audioRecord_ref, event, 0,0, NULL);
130            if (env->ExceptionCheck()) {
131                env->ExceptionDescribe();
132                env->ExceptionClear();
133            }
134        }
135        } break;
136    }
137
138    {
139        Mutex::Autolock l(sLock);
140        callbackInfo->busy = false;
141        callbackInfo->cond.broadcast();
142    }
143}
144
145// ----------------------------------------------------------------------------
146static sp<AudioRecord> getAudioRecord(JNIEnv* env, jobject thiz)
147{
148    Mutex::Autolock l(sLock);
149    AudioRecord* const ar =
150            (AudioRecord*)env->GetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
151    return sp<AudioRecord>(ar);
152}
153
154static sp<AudioRecord> setAudioRecord(JNIEnv* env, jobject thiz, const sp<AudioRecord>& ar)
155{
156    Mutex::Autolock l(sLock);
157    sp<AudioRecord> old =
158            (AudioRecord*)env->GetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
159    if (ar.get()) {
160        ar->incStrong((void*)setAudioRecord);
161    }
162    if (old != 0) {
163        old->decStrong((void*)setAudioRecord);
164    }
165    env->SetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj, (int)ar.get());
166    return old;
167}
168
169// ----------------------------------------------------------------------------
170static int
171android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject weak_this,
172        jint source, jint sampleRateInHertz, jint channelMask,
173                // Java channel masks map directly to the native definition
174        jint audioFormat, jint buffSizeInBytes, jintArray jSession)
175{
176    //ALOGV(">> Entering android_media_AudioRecord_setup");
177    //ALOGV("sampleRate=%d, audioFormat=%d, channel mask=%x, buffSizeInBytes=%d",
178    //     sampleRateInHertz, audioFormat, channelMask, buffSizeInBytes);
179
180    if (!audio_is_input_channel(channelMask)) {
181        ALOGE("Error creating AudioRecord: channel mask %#x is not valid.", channelMask);
182        return AUDIORECORD_ERROR_SETUP_INVALIDCHANNELMASK;
183    }
184    uint32_t nbChannels = popcount(channelMask);
185
186    // compare the format against the Java constants
187    audio_format_t format = audioFormatToNative(audioFormat);
188    if (format == AUDIO_FORMAT_INVALID) {
189        ALOGE("Error creating AudioRecord: unsupported audio format %d.", audioFormat);
190        return AUDIORECORD_ERROR_SETUP_INVALIDFORMAT;
191    }
192
193    size_t bytesPerSample = audio_bytes_per_sample(format);
194
195    if (buffSizeInBytes == 0) {
196         ALOGE("Error creating AudioRecord: frameCount is 0.");
197        return AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT;
198    }
199    size_t frameSize = nbChannels * bytesPerSample;
200    size_t frameCount = buffSizeInBytes / frameSize;
201
202    if ((uint32_t(source) >= AUDIO_SOURCE_CNT) && (uint32_t(source) != AUDIO_SOURCE_HOTWORD)) {
203        ALOGE("Error creating AudioRecord: unknown source %d.", source);
204        return AUDIORECORD_ERROR_SETUP_INVALIDSOURCE;
205    }
206
207    jclass clazz = env->GetObjectClass(thiz);
208    if (clazz == NULL) {
209        ALOGE("Can't find %s when setting up callback.", kClassPathName);
210        return AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED;
211    }
212
213    if (jSession == NULL) {
214        ALOGE("Error creating AudioRecord: invalid session ID pointer");
215        return AUDIORECORD_ERROR;
216    }
217
218    jint* nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
219    if (nSession == NULL) {
220        ALOGE("Error creating AudioRecord: Error retrieving session id pointer");
221        return AUDIORECORD_ERROR;
222    }
223    int sessionId = nSession[0];
224    env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
225    nSession = NULL;
226
227    // create an uninitialized AudioRecord object
228    sp<AudioRecord> lpRecorder = new AudioRecord();
229
230    // create the callback information:
231    // this data will be passed with every AudioRecord callback
232    audiorecord_callback_cookie *lpCallbackData = new audiorecord_callback_cookie;
233    lpCallbackData->audioRecord_class = (jclass)env->NewGlobalRef(clazz);
234    // we use a weak reference so the AudioRecord object can be garbage collected.
235    lpCallbackData->audioRecord_ref = env->NewGlobalRef(weak_this);
236    lpCallbackData->busy = false;
237
238    lpRecorder->set((audio_source_t) source,
239        sampleRateInHertz,
240        format,        // word length, PCM
241        channelMask,
242        frameCount,
243        recorderCallback,// callback_t
244        lpCallbackData,// void* user
245        0,             // notificationFrames,
246        true,          // threadCanCallJava
247        sessionId);
248
249    const status_t status = lpRecorder->initCheck();
250    if (status != NO_ERROR) {
251        ALOGE("Error creating AudioRecord instance: initialization check failed with status %d.",
252                status);
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    if (readSize < 0) {
402        readSize = AUDIORECORD_ERROR_INVALID_OPERATION;
403    }
404    return (jint) readSize;
405}
406
407// ----------------------------------------------------------------------------
408static jint android_media_AudioRecord_readInShortArray(JNIEnv *env,  jobject thiz,
409                                                        jshortArray javaAudioData,
410                                                        jint offsetInShorts, jint sizeInShorts) {
411
412    jint read = android_media_AudioRecord_readInByteArray(env, thiz,
413                                                        (jbyteArray) javaAudioData,
414                                                        offsetInShorts*2, sizeInShorts*2);
415    if (read > 0) {
416        read /= 2;
417    }
418    return read;
419}
420
421// ----------------------------------------------------------------------------
422static jint android_media_AudioRecord_readInDirectBuffer(JNIEnv *env,  jobject thiz,
423                                                  jobject jBuffer, jint sizeInBytes) {
424    // get the audio recorder from which we'll read new audio samples
425    sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
426    if (lpRecorder==NULL)
427        return 0;
428
429    // direct buffer and direct access supported?
430    long capacity = env->GetDirectBufferCapacity(jBuffer);
431    if (capacity == -1) {
432        // buffer direct access is not supported
433        ALOGE("Buffer direct access is not supported, can't record");
434        return 0;
435    }
436    //ALOGV("capacity = %ld", capacity);
437    jbyte* nativeFromJavaBuf = (jbyte*) env->GetDirectBufferAddress(jBuffer);
438    if (nativeFromJavaBuf==NULL) {
439        ALOGE("Buffer direct access is not supported, can't record");
440        return 0;
441    }
442
443    // read new data from the recorder
444    ssize_t readSize = lpRecorder->read(nativeFromJavaBuf,
445                                   capacity < sizeInBytes ? capacity : sizeInBytes);
446    if (readSize < 0) {
447        readSize = AUDIORECORD_ERROR_INVALID_OPERATION;
448    }
449    return (jint)readSize;
450}
451
452
453// ----------------------------------------------------------------------------
454static jint android_media_AudioRecord_set_marker_pos(JNIEnv *env,  jobject thiz,
455        jint markerPos) {
456
457    sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
458    if (lpRecorder == NULL) {
459        jniThrowException(env, "java/lang/IllegalStateException",
460            "Unable to retrieve AudioRecord pointer for setMarkerPosition()");
461        return AUDIORECORD_ERROR;
462    }
463    return android_media_translateRecorderErrorCode( lpRecorder->setMarkerPosition(markerPos) );
464}
465
466
467// ----------------------------------------------------------------------------
468static jint android_media_AudioRecord_get_marker_pos(JNIEnv *env,  jobject thiz) {
469
470    sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
471    uint32_t markerPos = 0;
472
473    if (lpRecorder == NULL) {
474        jniThrowException(env, "java/lang/IllegalStateException",
475            "Unable to retrieve AudioRecord pointer for getMarkerPosition()");
476        return AUDIORECORD_ERROR;
477    }
478    lpRecorder->getMarkerPosition(&markerPos);
479    return (jint)markerPos;
480}
481
482
483// ----------------------------------------------------------------------------
484static jint android_media_AudioRecord_set_pos_update_period(JNIEnv *env,  jobject thiz,
485        jint period) {
486
487    sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
488
489    if (lpRecorder == NULL) {
490        jniThrowException(env, "java/lang/IllegalStateException",
491            "Unable to retrieve AudioRecord pointer for setPositionUpdatePeriod()");
492        return AUDIORECORD_ERROR;
493    }
494    return android_media_translateRecorderErrorCode( lpRecorder->setPositionUpdatePeriod(period) );
495}
496
497
498// ----------------------------------------------------------------------------
499static jint android_media_AudioRecord_get_pos_update_period(JNIEnv *env,  jobject thiz) {
500
501    sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
502    uint32_t period = 0;
503
504    if (lpRecorder == NULL) {
505        jniThrowException(env, "java/lang/IllegalStateException",
506            "Unable to retrieve AudioRecord pointer for getPositionUpdatePeriod()");
507        return AUDIORECORD_ERROR;
508    }
509    lpRecorder->getPositionUpdatePeriod(&period);
510    return (jint)period;
511}
512
513
514// ----------------------------------------------------------------------------
515// returns the minimum required size for the successful creation of an AudioRecord instance.
516// returns 0 if the parameter combination is not supported.
517// return -1 if there was an error querying the buffer size.
518static jint android_media_AudioRecord_get_min_buff_size(JNIEnv *env,  jobject thiz,
519    jint sampleRateInHertz, jint nbChannels, jint audioFormat) {
520
521    ALOGV(">> android_media_AudioRecord_get_min_buff_size(%d, %d, %d)",
522          sampleRateInHertz, nbChannels, audioFormat);
523
524    size_t frameCount = 0;
525    audio_format_t format = audioFormatToNative(audioFormat);
526    status_t result = AudioRecord::getMinFrameCount(&frameCount,
527            sampleRateInHertz,
528            format,
529            audio_channel_in_mask_from_count(nbChannels));
530
531    if (result == BAD_VALUE) {
532        return 0;
533    }
534    if (result != NO_ERROR) {
535        return -1;
536    }
537    return frameCount * nbChannels * audio_bytes_per_sample(format);
538}
539
540
541// ----------------------------------------------------------------------------
542// ----------------------------------------------------------------------------
543static JNINativeMethod gMethods[] = {
544    // name,               signature,  funcPtr
545    {"native_start",         "(II)I",    (void *)android_media_AudioRecord_start},
546    {"native_stop",          "()V",    (void *)android_media_AudioRecord_stop},
547    {"native_setup",         "(Ljava/lang/Object;IIIII[I)I",
548                                       (void *)android_media_AudioRecord_setup},
549    {"native_finalize",      "()V",    (void *)android_media_AudioRecord_finalize},
550    {"native_release",       "()V",    (void *)android_media_AudioRecord_release},
551    {"native_read_in_byte_array",
552                             "([BII)I", (void *)android_media_AudioRecord_readInByteArray},
553    {"native_read_in_short_array",
554                             "([SII)I", (void *)android_media_AudioRecord_readInShortArray},
555    {"native_read_in_direct_buffer","(Ljava/lang/Object;I)I",
556                                       (void *)android_media_AudioRecord_readInDirectBuffer},
557    {"native_set_marker_pos","(I)I",   (void *)android_media_AudioRecord_set_marker_pos},
558    {"native_get_marker_pos","()I",    (void *)android_media_AudioRecord_get_marker_pos},
559    {"native_set_pos_update_period",
560                             "(I)I",   (void *)android_media_AudioRecord_set_pos_update_period},
561    {"native_get_pos_update_period",
562                             "()I",    (void *)android_media_AudioRecord_get_pos_update_period},
563    {"native_get_min_buff_size",
564                             "(III)I",   (void *)android_media_AudioRecord_get_min_buff_size},
565};
566
567// field names found in android/media/AudioRecord.java
568#define JAVA_POSTEVENT_CALLBACK_NAME  "postEventFromNative"
569#define JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME  "mNativeRecorderInJavaObj"
570#define JAVA_NATIVECALLBACKINFO_FIELD_NAME       "mNativeCallbackCookie"
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    return AndroidRuntime::registerNativeMethods(env,
614            kClassPathName, gMethods, NELEM(gMethods));
615}
616
617// ----------------------------------------------------------------------------
618