android_media_AudioRecord.cpp revision 95ece35c2f8b86203e88b49d67cef8e80e107a2b
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 <inttypes.h>
22#include <jni.h>
23#include <JNIHelp.h>
24#include <android_runtime/AndroidRuntime.h>
25
26#include <utils/Log.h>
27#include <media/AudioRecord.h>
28
29#include <system/audio.h>
30
31// ----------------------------------------------------------------------------
32
33using namespace android;
34
35// ----------------------------------------------------------------------------
36static const char* const kClassPathName = "android/media/AudioRecord";
37
38struct fields_t {
39    // these fields provide access from C++ to the...
40    jmethodID postNativeEventInJava; //... event post callback method
41    jfieldID  nativeRecorderInJavaObj; // provides access to the C++ AudioRecord object
42    jfieldID  nativeCallbackCookie;    // provides access to the AudioRecord callback data
43};
44static fields_t javaAudioRecordFields;
45
46struct audiorecord_callback_cookie {
47    jclass      audioRecord_class;
48    jobject     audioRecord_ref;
49    bool        busy;
50    Condition   cond;
51};
52
53// keep these values in sync with AudioFormat.java
54#define ENCODING_PCM_16BIT 2
55#define ENCODING_PCM_8BIT  3
56
57static Mutex sLock;
58static SortedVector <audiorecord_callback_cookie *> sAudioRecordCallBackCookies;
59
60// ----------------------------------------------------------------------------
61
62#define AUDIORECORD_SUCCESS                         0
63#define AUDIORECORD_ERROR                           -1
64#define AUDIORECORD_ERROR_BAD_VALUE                 -2
65#define AUDIORECORD_ERROR_INVALID_OPERATION         -3
66#define AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT      -16
67#define AUDIORECORD_ERROR_SETUP_INVALIDCHANNELMASK -17
68#define AUDIORECORD_ERROR_SETUP_INVALIDFORMAT       -18
69#define AUDIORECORD_ERROR_SETUP_INVALIDSOURCE       -19
70#define AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED    -20
71
72jint android_media_translateRecorderErrorCode(int code) {
73    switch (code) {
74    case NO_ERROR:
75        return AUDIORECORD_SUCCESS;
76    case BAD_VALUE:
77        return AUDIORECORD_ERROR_BAD_VALUE;
78    case INVALID_OPERATION:
79        return AUDIORECORD_ERROR_INVALID_OPERATION;
80    default:
81        return AUDIORECORD_ERROR;
82    }
83}
84
85
86// ----------------------------------------------------------------------------
87static void recorderCallback(int event, void* user, void *info) {
88
89    audiorecord_callback_cookie *callbackInfo = (audiorecord_callback_cookie *)user;
90    {
91        Mutex::Autolock l(sLock);
92        if (sAudioRecordCallBackCookies.indexOf(callbackInfo) < 0) {
93            return;
94        }
95        callbackInfo->busy = true;
96    }
97
98    switch (event) {
99    case AudioRecord::EVENT_MARKER: {
100        JNIEnv *env = AndroidRuntime::getJNIEnv();
101        if (user != NULL && env != NULL) {
102            env->CallStaticVoidMethod(
103                callbackInfo->audioRecord_class,
104                javaAudioRecordFields.postNativeEventInJava,
105                callbackInfo->audioRecord_ref, event, 0,0, NULL);
106            if (env->ExceptionCheck()) {
107                env->ExceptionDescribe();
108                env->ExceptionClear();
109            }
110        }
111        } break;
112
113    case AudioRecord::EVENT_NEW_POS: {
114        JNIEnv *env = AndroidRuntime::getJNIEnv();
115        if (user != NULL && env != NULL) {
116            env->CallStaticVoidMethod(
117                callbackInfo->audioRecord_class,
118                javaAudioRecordFields.postNativeEventInJava,
119                callbackInfo->audioRecord_ref, event, 0,0, NULL);
120            if (env->ExceptionCheck()) {
121                env->ExceptionDescribe();
122                env->ExceptionClear();
123            }
124        }
125        } break;
126    }
127
128    {
129        Mutex::Autolock l(sLock);
130        callbackInfo->busy = false;
131        callbackInfo->cond.broadcast();
132    }
133}
134
135// ----------------------------------------------------------------------------
136static sp<AudioRecord> getAudioRecord(JNIEnv* env, jobject thiz)
137{
138    Mutex::Autolock l(sLock);
139    AudioRecord* const ar =
140            (AudioRecord*)env->GetLongField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
141    return sp<AudioRecord>(ar);
142}
143
144static sp<AudioRecord> setAudioRecord(JNIEnv* env, jobject thiz, const sp<AudioRecord>& ar)
145{
146    Mutex::Autolock l(sLock);
147    sp<AudioRecord> old =
148            (AudioRecord*)env->GetLongField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
149    if (ar.get()) {
150        ar->incStrong((void*)setAudioRecord);
151    }
152    if (old != 0) {
153        old->decStrong((void*)setAudioRecord);
154    }
155    env->SetLongField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj, (jlong)ar.get());
156    return old;
157}
158
159// ----------------------------------------------------------------------------
160static jint
161android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject weak_this,
162        jint source, jint sampleRateInHertz, jint channelMask,
163                // Java channel masks map directly to the native definition
164        jint audioFormat, jint buffSizeInBytes, jintArray jSession)
165{
166    //ALOGV(">> Entering android_media_AudioRecord_setup");
167    //ALOGV("sampleRate=%d, audioFormat=%d, channel mask=%x, buffSizeInBytes=%d",
168    //     sampleRateInHertz, audioFormat, channelMask, buffSizeInBytes);
169
170    if (!audio_is_input_channel(channelMask)) {
171        ALOGE("Error creating AudioRecord: channel mask %#x is not valid.", channelMask);
172        return (jint) AUDIORECORD_ERROR_SETUP_INVALIDCHANNELMASK;
173    }
174    uint32_t nbChannels = popcount(channelMask);
175
176    // compare the format against the Java constants
177    if ((audioFormat != ENCODING_PCM_16BIT)
178        && (audioFormat != ENCODING_PCM_8BIT)) {
179        ALOGE("Error creating AudioRecord: unsupported audio format.");
180        return (jint) AUDIORECORD_ERROR_SETUP_INVALIDFORMAT;
181    }
182
183    int bytesPerSample = audioFormat == ENCODING_PCM_16BIT ? 2 : 1;
184    audio_format_t format = audioFormat == ENCODING_PCM_16BIT ?
185            AUDIO_FORMAT_PCM_16_BIT : AUDIO_FORMAT_PCM_8_BIT;
186
187    if (buffSizeInBytes == 0) {
188         ALOGE("Error creating AudioRecord: frameCount is 0.");
189        return (jint) AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT;
190    }
191    int frameSize = nbChannels * bytesPerSample;
192    size_t frameCount = buffSizeInBytes / frameSize;
193
194    if ((uint32_t(source) >= AUDIO_SOURCE_CNT) && (uint32_t(source) != AUDIO_SOURCE_HOTWORD)) {
195        ALOGE("Error creating AudioRecord: unknown source.");
196        return (jint) AUDIORECORD_ERROR_SETUP_INVALIDSOURCE;
197    }
198
199    jclass clazz = env->GetObjectClass(thiz);
200    if (clazz == NULL) {
201        ALOGE("Can't find %s when setting up callback.", kClassPathName);
202        return (jint) AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED;
203    }
204
205    if (jSession == NULL) {
206        ALOGE("Error creating AudioRecord: invalid session ID pointer");
207        return (jint) AUDIORECORD_ERROR;
208    }
209
210    jint* nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
211    if (nSession == NULL) {
212        ALOGE("Error creating AudioRecord: Error retrieving session id pointer");
213        return (jint) AUDIORECORD_ERROR;
214    }
215    int sessionId = nSession[0];
216    env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
217    nSession = NULL;
218
219    // create an uninitialized AudioRecord object
220    sp<AudioRecord> lpRecorder = new AudioRecord();
221
222    // create the callback information:
223    // this data will be passed with every AudioRecord callback
224    audiorecord_callback_cookie *lpCallbackData = new audiorecord_callback_cookie;
225    lpCallbackData->audioRecord_class = (jclass)env->NewGlobalRef(clazz);
226    // we use a weak reference so the AudioRecord object can be garbage collected.
227    lpCallbackData->audioRecord_ref = env->NewGlobalRef(weak_this);
228    lpCallbackData->busy = false;
229
230    lpRecorder->set((audio_source_t) source,
231        sampleRateInHertz,
232        format,        // word length, PCM
233        channelMask,
234        frameCount,
235        recorderCallback,// callback_t
236        lpCallbackData,// void* user
237        0,             // notificationFrames,
238        true,          // threadCanCallJava
239        sessionId);
240
241    if (lpRecorder->initCheck() != NO_ERROR) {
242        ALOGE("Error creating AudioRecord instance: initialization check failed.");
243        goto native_init_failure;
244    }
245
246    nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
247    if (nSession == NULL) {
248        ALOGE("Error creating AudioRecord: Error retrieving session id pointer");
249        goto native_init_failure;
250    }
251    // read the audio session ID back from AudioRecord in case a new session was created during set()
252    nSession[0] = lpRecorder->getSessionId();
253    env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
254    nSession = NULL;
255
256    {   // scope for the lock
257        Mutex::Autolock l(sLock);
258        sAudioRecordCallBackCookies.add(lpCallbackData);
259    }
260    // save our newly created C++ AudioRecord in the "nativeRecorderInJavaObj" field
261    // of the Java object
262    setAudioRecord(env, thiz, lpRecorder);
263
264    // save our newly created callback information in the "nativeCallbackCookie" field
265    // of the Java object (in mNativeCallbackCookie) so we can free the memory in finalize()
266    env->SetLongField(thiz, javaAudioRecordFields.nativeCallbackCookie, (jlong)lpCallbackData);
267
268    return (jint) AUDIORECORD_SUCCESS;
269
270    // failure:
271native_init_failure:
272    env->DeleteGlobalRef(lpCallbackData->audioRecord_class);
273    env->DeleteGlobalRef(lpCallbackData->audioRecord_ref);
274    delete lpCallbackData;
275    env->SetLongField(thiz, javaAudioRecordFields.nativeCallbackCookie, 0);
276
277    return (jint) AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED;
278}
279
280
281
282// ----------------------------------------------------------------------------
283static jint
284android_media_AudioRecord_start(JNIEnv *env, jobject thiz, jint event, jint triggerSession)
285{
286    sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
287    if (lpRecorder == NULL ) {
288        jniThrowException(env, "java/lang/IllegalStateException", NULL);
289        return (jint) AUDIORECORD_ERROR;
290    }
291
292    return (jint) android_media_translateRecorderErrorCode(
293            lpRecorder->start((AudioSystem::sync_event_t)event, triggerSession));
294}
295
296
297// ----------------------------------------------------------------------------
298static void
299android_media_AudioRecord_stop(JNIEnv *env, jobject thiz)
300{
301    sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
302    if (lpRecorder == NULL ) {
303        jniThrowException(env, "java/lang/IllegalStateException", NULL);
304        return;
305    }
306
307    lpRecorder->stop();
308    //ALOGV("Called lpRecorder->stop()");
309}
310
311
312// ----------------------------------------------------------------------------
313
314#define CALLBACK_COND_WAIT_TIMEOUT_MS 1000
315static void android_media_AudioRecord_release(JNIEnv *env,  jobject thiz) {
316    sp<AudioRecord> lpRecorder = setAudioRecord(env, thiz, 0);
317    if (lpRecorder == NULL) {
318        return;
319    }
320    ALOGV("About to delete lpRecorder: %" PRIxPTR "\n", lpRecorder.get());
321    lpRecorder->stop();
322
323    audiorecord_callback_cookie *lpCookie = (audiorecord_callback_cookie *)env->GetLongField(
324        thiz, javaAudioRecordFields.nativeCallbackCookie);
325
326    // reset the native resources in the Java object so any attempt to access
327    // them after a call to release fails.
328    env->SetLongField(thiz, javaAudioRecordFields.nativeCallbackCookie, 0);
329
330    // delete the callback information
331    if (lpCookie) {
332        Mutex::Autolock l(sLock);
333        ALOGV("deleting lpCookie: %" PRIxPTR "\n", lpCookie);
334        while (lpCookie->busy) {
335            if (lpCookie->cond.waitRelative(sLock,
336                                            milliseconds(CALLBACK_COND_WAIT_TIMEOUT_MS)) !=
337                                                    NO_ERROR) {
338                break;
339            }
340        }
341        sAudioRecordCallBackCookies.remove(lpCookie);
342        env->DeleteGlobalRef(lpCookie->audioRecord_class);
343        env->DeleteGlobalRef(lpCookie->audioRecord_ref);
344        delete lpCookie;
345    }
346}
347
348
349// ----------------------------------------------------------------------------
350static void android_media_AudioRecord_finalize(JNIEnv *env,  jobject thiz) {
351    android_media_AudioRecord_release(env, thiz);
352}
353
354
355// ----------------------------------------------------------------------------
356static jint android_media_AudioRecord_readInByteArray(JNIEnv *env,  jobject thiz,
357                                                        jbyteArray javaAudioData,
358                                                        jint offsetInBytes, jint sizeInBytes) {
359    jbyte* recordBuff = NULL;
360    // get the audio recorder from which we'll read new audio samples
361    sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
362    if (lpRecorder == NULL) {
363        ALOGE("Unable to retrieve AudioRecord object, can't record");
364        return 0;
365    }
366
367    if (!javaAudioData) {
368        ALOGE("Invalid Java array to store recorded audio, can't record");
369        return 0;
370    }
371
372    // get the pointer to where we'll record the audio
373    // NOTE: We may use GetPrimitiveArrayCritical() when the JNI implementation changes in such
374    // a way that it becomes much more efficient. When doing so, we will have to prevent the
375    // AudioSystem callback to be called while in critical section (in case of media server
376    // process crash for instance)
377    recordBuff = (jbyte *)env->GetByteArrayElements(javaAudioData, NULL);
378
379    if (recordBuff == NULL) {
380        ALOGE("Error retrieving destination for recorded audio data, can't record");
381        return 0;
382    }
383
384    // read the new audio data from the native AudioRecord object
385    ssize_t recorderBuffSize = lpRecorder->frameCount()*lpRecorder->frameSize();
386    ssize_t readSize = lpRecorder->read(recordBuff + offsetInBytes,
387                                        sizeInBytes > (jint)recorderBuffSize ?
388                                            (jint)recorderBuffSize : sizeInBytes );
389    env->ReleaseByteArrayElements(javaAudioData, recordBuff, 0);
390
391    if (readSize < 0) {
392        readSize = AUDIORECORD_ERROR_INVALID_OPERATION;
393    }
394    return (jint) readSize;
395}
396
397// ----------------------------------------------------------------------------
398static jint android_media_AudioRecord_readInShortArray(JNIEnv *env,  jobject thiz,
399                                                        jshortArray javaAudioData,
400                                                        jint offsetInShorts, jint sizeInShorts) {
401
402    jint read = android_media_AudioRecord_readInByteArray(env, thiz,
403                                                        (jbyteArray) javaAudioData,
404                                                        offsetInShorts*2, sizeInShorts*2);
405    if (read > 0) {
406        read /= 2;
407    }
408    return read;
409}
410
411// ----------------------------------------------------------------------------
412static jint android_media_AudioRecord_readInDirectBuffer(JNIEnv *env,  jobject thiz,
413                                                  jobject jBuffer, jint sizeInBytes) {
414    // get the audio recorder from which we'll read new audio samples
415    sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
416    if (lpRecorder==NULL)
417        return 0;
418
419    // direct buffer and direct access supported?
420    long capacity = env->GetDirectBufferCapacity(jBuffer);
421    if (capacity == -1) {
422        // buffer direct access is not supported
423        ALOGE("Buffer direct access is not supported, can't record");
424        return 0;
425    }
426    //ALOGV("capacity = %ld", capacity);
427    jbyte* nativeFromJavaBuf = (jbyte*) env->GetDirectBufferAddress(jBuffer);
428    if (nativeFromJavaBuf==NULL) {
429        ALOGE("Buffer direct access is not supported, can't record");
430        return 0;
431    }
432
433    // read new data from the recorder
434    ssize_t readSize = lpRecorder->read(nativeFromJavaBuf,
435                                   capacity < sizeInBytes ? capacity : sizeInBytes);
436    if (readSize < 0) {
437        readSize = AUDIORECORD_ERROR_INVALID_OPERATION;
438    }
439    return (jint)readSize;
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    size_t frameCount = 0;
515    status_t result = AudioRecord::getMinFrameCount(&frameCount,
516            sampleRateInHertz,
517            (audioFormat == ENCODING_PCM_16BIT ?
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 == ENCODING_PCM_16BIT ? 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_NATIVERECORDERINJAVAOBJ_FIELD_NAME  "mNativeRecorderInJavaObj"
560#define JAVA_NATIVECALLBACKINFO_FIELD_NAME       "mNativeCallbackCookie"
561
562// ----------------------------------------------------------------------------
563int register_android_media_AudioRecord(JNIEnv *env)
564{
565    javaAudioRecordFields.postNativeEventInJava = NULL;
566    javaAudioRecordFields.nativeRecorderInJavaObj = NULL;
567    javaAudioRecordFields.nativeCallbackCookie = NULL;
568
569
570    // Get the AudioRecord class
571    jclass audioRecordClass = env->FindClass(kClassPathName);
572    if (audioRecordClass == NULL) {
573        ALOGE("Can't find %s", kClassPathName);
574        return -1;
575    }
576    // Get the postEvent method
577    javaAudioRecordFields.postNativeEventInJava = env->GetStaticMethodID(
578            audioRecordClass,
579            JAVA_POSTEVENT_CALLBACK_NAME, "(Ljava/lang/Object;IIILjava/lang/Object;)V");
580    if (javaAudioRecordFields.postNativeEventInJava == NULL) {
581        ALOGE("Can't find AudioRecord.%s", JAVA_POSTEVENT_CALLBACK_NAME);
582        return -1;
583    }
584
585    // Get the variables
586    //    mNativeRecorderInJavaObj
587    javaAudioRecordFields.nativeRecorderInJavaObj =
588        env->GetFieldID(audioRecordClass,
589                        JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME, "J");
590    if (javaAudioRecordFields.nativeRecorderInJavaObj == NULL) {
591        ALOGE("Can't find AudioRecord.%s", JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME);
592        return -1;
593    }
594    //     mNativeCallbackCookie
595    javaAudioRecordFields.nativeCallbackCookie = env->GetFieldID(
596            audioRecordClass,
597            JAVA_NATIVECALLBACKINFO_FIELD_NAME, "J");
598    if (javaAudioRecordFields.nativeCallbackCookie == NULL) {
599        ALOGE("Can't find AudioRecord.%s", JAVA_NATIVECALLBACKINFO_FIELD_NAME);
600        return -1;
601    }
602
603    return AndroidRuntime::registerNativeMethods(env,
604            kClassPathName, gMethods, NELEM(gMethods));
605}
606
607// ----------------------------------------------------------------------------
608