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