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