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