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