android_media_AudioRecord.cpp revision c5d142428ad68dd8aa4ffb80719667b13ad5049f
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 nbChannels = 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 = nbChannels * 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    const status_t status = 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    if (status != NO_ERROR) {
235        ALOGE("Error creating AudioRecord instance: initialization check failed with status %d.",
236                status);
237        goto native_init_failure;
238    }
239
240    nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
241    if (nSession == NULL) {
242        ALOGE("Error creating AudioRecord: Error retrieving session id pointer");
243        goto native_init_failure;
244    }
245    // read the audio session ID back from AudioRecord in case a new session was created during set()
246    nSession[0] = lpRecorder->getSessionId();
247    env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
248    nSession = NULL;
249
250    {   // scope for the lock
251        Mutex::Autolock l(sLock);
252        sAudioRecordCallBackCookies.add(lpCallbackData);
253    }
254    // save our newly created C++ AudioRecord in the "nativeRecorderInJavaObj" field
255    // of the Java object
256    setAudioRecord(env, thiz, lpRecorder);
257
258    // save our newly created callback information in the "nativeCallbackCookie" field
259    // of the Java object (in mNativeCallbackCookie) so we can free the memory in finalize()
260    env->SetLongField(thiz, javaAudioRecordFields.nativeCallbackCookie, (jlong)lpCallbackData);
261
262    return (jint) AUDIORECORD_SUCCESS;
263
264    // failure:
265native_init_failure:
266    env->DeleteGlobalRef(lpCallbackData->audioRecord_class);
267    env->DeleteGlobalRef(lpCallbackData->audioRecord_ref);
268    delete lpCallbackData;
269    env->SetLongField(thiz, javaAudioRecordFields.nativeCallbackCookie, 0);
270
271    return (jint) AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED;
272}
273
274
275
276// ----------------------------------------------------------------------------
277static jint
278android_media_AudioRecord_start(JNIEnv *env, jobject thiz, jint event, jint triggerSession)
279{
280    sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
281    if (lpRecorder == NULL ) {
282        jniThrowException(env, "java/lang/IllegalStateException", NULL);
283        return (jint) AUDIORECORD_ERROR;
284    }
285
286    return (jint) android_media_translateRecorderErrorCode(
287            lpRecorder->start((AudioSystem::sync_event_t)event, triggerSession));
288}
289
290
291// ----------------------------------------------------------------------------
292static void
293android_media_AudioRecord_stop(JNIEnv *env, jobject thiz)
294{
295    sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
296    if (lpRecorder == NULL ) {
297        jniThrowException(env, "java/lang/IllegalStateException", NULL);
298        return;
299    }
300
301    lpRecorder->stop();
302    //ALOGV("Called lpRecorder->stop()");
303}
304
305
306// ----------------------------------------------------------------------------
307
308#define CALLBACK_COND_WAIT_TIMEOUT_MS 1000
309static void android_media_AudioRecord_release(JNIEnv *env,  jobject thiz) {
310    sp<AudioRecord> lpRecorder = setAudioRecord(env, thiz, 0);
311    if (lpRecorder == NULL) {
312        return;
313    }
314    ALOGV("About to delete lpRecorder: %" PRIxPTR "\n", lpRecorder.get());
315    lpRecorder->stop();
316
317    audiorecord_callback_cookie *lpCookie = (audiorecord_callback_cookie *)env->GetLongField(
318        thiz, javaAudioRecordFields.nativeCallbackCookie);
319
320    // reset the native resources in the Java object so any attempt to access
321    // them after a call to release fails.
322    env->SetLongField(thiz, javaAudioRecordFields.nativeCallbackCookie, 0);
323
324    // delete the callback information
325    if (lpCookie) {
326        Mutex::Autolock l(sLock);
327        ALOGV("deleting lpCookie: %" PRIxPTR "\n", lpCookie);
328        while (lpCookie->busy) {
329            if (lpCookie->cond.waitRelative(sLock,
330                                            milliseconds(CALLBACK_COND_WAIT_TIMEOUT_MS)) !=
331                                                    NO_ERROR) {
332                break;
333            }
334        }
335        sAudioRecordCallBackCookies.remove(lpCookie);
336        env->DeleteGlobalRef(lpCookie->audioRecord_class);
337        env->DeleteGlobalRef(lpCookie->audioRecord_ref);
338        delete lpCookie;
339    }
340}
341
342
343// ----------------------------------------------------------------------------
344static void android_media_AudioRecord_finalize(JNIEnv *env,  jobject thiz) {
345    android_media_AudioRecord_release(env, thiz);
346}
347
348
349// ----------------------------------------------------------------------------
350static jint android_media_AudioRecord_readInByteArray(JNIEnv *env,  jobject thiz,
351                                                        jbyteArray javaAudioData,
352                                                        jint offsetInBytes, jint sizeInBytes) {
353    jbyte* recordBuff = NULL;
354    // get the audio recorder from which we'll read new audio samples
355    sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
356    if (lpRecorder == NULL) {
357        ALOGE("Unable to retrieve AudioRecord object, can't record");
358        return 0;
359    }
360
361    if (!javaAudioData) {
362        ALOGE("Invalid Java array to store recorded audio, can't record");
363        return 0;
364    }
365
366    // get the pointer to where we'll record the audio
367    // NOTE: We may use GetPrimitiveArrayCritical() when the JNI implementation changes in such
368    // a way that it becomes much more efficient. When doing so, we will have to prevent the
369    // AudioSystem callback to be called while in critical section (in case of media server
370    // process crash for instance)
371    recordBuff = (jbyte *)env->GetByteArrayElements(javaAudioData, NULL);
372
373    if (recordBuff == NULL) {
374        ALOGE("Error retrieving destination for recorded audio data, can't record");
375        return 0;
376    }
377
378    // read the new audio data from the native AudioRecord object
379    ssize_t recorderBuffSize = lpRecorder->frameCount()*lpRecorder->frameSize();
380    ssize_t readSize = lpRecorder->read(recordBuff + offsetInBytes,
381                                        sizeInBytes > (jint)recorderBuffSize ?
382                                            (jint)recorderBuffSize : sizeInBytes );
383    env->ReleaseByteArrayElements(javaAudioData, recordBuff, 0);
384
385    if (readSize < 0) {
386        readSize = AUDIORECORD_ERROR_INVALID_OPERATION;
387    }
388    return (jint) readSize;
389}
390
391// ----------------------------------------------------------------------------
392static jint android_media_AudioRecord_readInShortArray(JNIEnv *env,  jobject thiz,
393                                                        jshortArray javaAudioData,
394                                                        jint offsetInShorts, jint sizeInShorts) {
395
396    jint read = android_media_AudioRecord_readInByteArray(env, thiz,
397                                                        (jbyteArray) javaAudioData,
398                                                        offsetInShorts*2, sizeInShorts*2);
399    if (read > 0) {
400        read /= 2;
401    }
402    return read;
403}
404
405// ----------------------------------------------------------------------------
406static jint android_media_AudioRecord_readInDirectBuffer(JNIEnv *env,  jobject thiz,
407                                                  jobject jBuffer, jint sizeInBytes) {
408    // get the audio recorder from which we'll read new audio samples
409    sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
410    if (lpRecorder==NULL)
411        return 0;
412
413    // direct buffer and direct access supported?
414    long capacity = env->GetDirectBufferCapacity(jBuffer);
415    if (capacity == -1) {
416        // buffer direct access is not supported
417        ALOGE("Buffer direct access is not supported, can't record");
418        return 0;
419    }
420    //ALOGV("capacity = %ld", capacity);
421    jbyte* nativeFromJavaBuf = (jbyte*) env->GetDirectBufferAddress(jBuffer);
422    if (nativeFromJavaBuf==NULL) {
423        ALOGE("Buffer direct access is not supported, can't record");
424        return 0;
425    }
426
427    // read new data from the recorder
428    ssize_t readSize = lpRecorder->read(nativeFromJavaBuf,
429                                   capacity < sizeInBytes ? capacity : sizeInBytes);
430    if (readSize < 0) {
431        readSize = AUDIORECORD_ERROR_INVALID_OPERATION;
432    }
433    return (jint)readSize;
434}
435
436
437// ----------------------------------------------------------------------------
438static jint android_media_AudioRecord_set_marker_pos(JNIEnv *env,  jobject thiz,
439        jint markerPos) {
440
441    sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
442    if (lpRecorder == NULL) {
443        jniThrowException(env, "java/lang/IllegalStateException",
444            "Unable to retrieve AudioRecord pointer for setMarkerPosition()");
445        return AUDIORECORD_ERROR;
446    }
447    return android_media_translateRecorderErrorCode( lpRecorder->setMarkerPosition(markerPos) );
448}
449
450
451// ----------------------------------------------------------------------------
452static jint android_media_AudioRecord_get_marker_pos(JNIEnv *env,  jobject thiz) {
453
454    sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
455    uint32_t markerPos = 0;
456
457    if (lpRecorder == NULL) {
458        jniThrowException(env, "java/lang/IllegalStateException",
459            "Unable to retrieve AudioRecord pointer for getMarkerPosition()");
460        return AUDIORECORD_ERROR;
461    }
462    lpRecorder->getMarkerPosition(&markerPos);
463    return (jint)markerPos;
464}
465
466
467// ----------------------------------------------------------------------------
468static jint android_media_AudioRecord_set_pos_update_period(JNIEnv *env,  jobject thiz,
469        jint period) {
470
471    sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
472
473    if (lpRecorder == NULL) {
474        jniThrowException(env, "java/lang/IllegalStateException",
475            "Unable to retrieve AudioRecord pointer for setPositionUpdatePeriod()");
476        return AUDIORECORD_ERROR;
477    }
478    return android_media_translateRecorderErrorCode( lpRecorder->setPositionUpdatePeriod(period) );
479}
480
481
482// ----------------------------------------------------------------------------
483static jint android_media_AudioRecord_get_pos_update_period(JNIEnv *env,  jobject thiz) {
484
485    sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
486    uint32_t period = 0;
487
488    if (lpRecorder == NULL) {
489        jniThrowException(env, "java/lang/IllegalStateException",
490            "Unable to retrieve AudioRecord pointer for getPositionUpdatePeriod()");
491        return AUDIORECORD_ERROR;
492    }
493    lpRecorder->getPositionUpdatePeriod(&period);
494    return (jint)period;
495}
496
497
498// ----------------------------------------------------------------------------
499// returns the minimum required size for the successful creation of an AudioRecord instance.
500// returns 0 if the parameter combination is not supported.
501// return -1 if there was an error querying the buffer size.
502static jint android_media_AudioRecord_get_min_buff_size(JNIEnv *env,  jobject thiz,
503    jint sampleRateInHertz, jint nbChannels, jint audioFormat) {
504
505    ALOGV(">> android_media_AudioRecord_get_min_buff_size(%d, %d, %d)",
506          sampleRateInHertz, nbChannels, audioFormat);
507
508    size_t frameCount = 0;
509    audio_format_t format = audioFormatToNative(audioFormat);
510    status_t result = AudioRecord::getMinFrameCount(&frameCount,
511            sampleRateInHertz,
512            format,
513            audio_channel_in_mask_from_count(nbChannels));
514
515    if (result == BAD_VALUE) {
516        return 0;
517    }
518    if (result != NO_ERROR) {
519        return -1;
520    }
521    return frameCount * nbChannels * audio_bytes_per_sample(format);
522}
523
524
525// ----------------------------------------------------------------------------
526// ----------------------------------------------------------------------------
527static JNINativeMethod gMethods[] = {
528    // name,               signature,  funcPtr
529    {"native_start",         "(II)I",    (void *)android_media_AudioRecord_start},
530    {"native_stop",          "()V",    (void *)android_media_AudioRecord_stop},
531    {"native_setup",         "(Ljava/lang/Object;IIIII[I)I",
532                                       (void *)android_media_AudioRecord_setup},
533    {"native_finalize",      "()V",    (void *)android_media_AudioRecord_finalize},
534    {"native_release",       "()V",    (void *)android_media_AudioRecord_release},
535    {"native_read_in_byte_array",
536                             "([BII)I", (void *)android_media_AudioRecord_readInByteArray},
537    {"native_read_in_short_array",
538                             "([SII)I", (void *)android_media_AudioRecord_readInShortArray},
539    {"native_read_in_direct_buffer","(Ljava/lang/Object;I)I",
540                                       (void *)android_media_AudioRecord_readInDirectBuffer},
541    {"native_set_marker_pos","(I)I",   (void *)android_media_AudioRecord_set_marker_pos},
542    {"native_get_marker_pos","()I",    (void *)android_media_AudioRecord_get_marker_pos},
543    {"native_set_pos_update_period",
544                             "(I)I",   (void *)android_media_AudioRecord_set_pos_update_period},
545    {"native_get_pos_update_period",
546                             "()I",    (void *)android_media_AudioRecord_get_pos_update_period},
547    {"native_get_min_buff_size",
548                             "(III)I",   (void *)android_media_AudioRecord_get_min_buff_size},
549};
550
551// field names found in android/media/AudioRecord.java
552#define JAVA_POSTEVENT_CALLBACK_NAME  "postEventFromNative"
553#define JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME  "mNativeRecorderInJavaObj"
554#define JAVA_NATIVECALLBACKINFO_FIELD_NAME       "mNativeCallbackCookie"
555
556// ----------------------------------------------------------------------------
557int register_android_media_AudioRecord(JNIEnv *env)
558{
559    javaAudioRecordFields.postNativeEventInJava = NULL;
560    javaAudioRecordFields.nativeRecorderInJavaObj = NULL;
561    javaAudioRecordFields.nativeCallbackCookie = NULL;
562
563
564    // Get the AudioRecord class
565    jclass audioRecordClass = env->FindClass(kClassPathName);
566    if (audioRecordClass == NULL) {
567        ALOGE("Can't find %s", kClassPathName);
568        return -1;
569    }
570    // Get the postEvent method
571    javaAudioRecordFields.postNativeEventInJava = env->GetStaticMethodID(
572            audioRecordClass,
573            JAVA_POSTEVENT_CALLBACK_NAME, "(Ljava/lang/Object;IIILjava/lang/Object;)V");
574    if (javaAudioRecordFields.postNativeEventInJava == NULL) {
575        ALOGE("Can't find AudioRecord.%s", JAVA_POSTEVENT_CALLBACK_NAME);
576        return -1;
577    }
578
579    // Get the variables
580    //    mNativeRecorderInJavaObj
581    javaAudioRecordFields.nativeRecorderInJavaObj =
582        env->GetFieldID(audioRecordClass,
583                        JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME, "J");
584    if (javaAudioRecordFields.nativeRecorderInJavaObj == NULL) {
585        ALOGE("Can't find AudioRecord.%s", JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME);
586        return -1;
587    }
588    //     mNativeCallbackCookie
589    javaAudioRecordFields.nativeCallbackCookie = env->GetFieldID(
590            audioRecordClass,
591            JAVA_NATIVECALLBACKINFO_FIELD_NAME, "J");
592    if (javaAudioRecordFields.nativeCallbackCookie == NULL) {
593        ALOGE("Can't find AudioRecord.%s", JAVA_NATIVECALLBACKINFO_FIELD_NAME);
594        return -1;
595    }
596
597    return AndroidRuntime::registerNativeMethods(env,
598            kClassPathName, gMethods, NELEM(gMethods));
599}
600
601// ----------------------------------------------------------------------------
602