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/AudioSystem.h"
32#include "media/AudioRecord.h"
33
34
35// ----------------------------------------------------------------------------
36
37using namespace android;
38
39// ----------------------------------------------------------------------------
40static const char* const kClassPathName = "android/media/AudioRecord";
41
42struct fields_t {
43    // these fields provide access from C++ to the...
44    jclass    audioRecordClass;      //... AudioRecord class
45    jmethodID postNativeEventInJava; //... event post callback method
46    int       PCM16;                 //...  format constants
47    int       PCM8;                  //...  format constants
48    jfieldID  nativeRecorderInJavaObj; // provides access to the C++ AudioRecord object
49    jfieldID  nativeCallbackCookie;    // provides access to the AudioRecord callback data
50};
51static fields_t javaAudioRecordFields;
52
53struct audiorecord_callback_cookie {
54    jclass      audioRecord_class;
55    jobject     audioRecord_ref;
56 };
57
58// ----------------------------------------------------------------------------
59
60#define AUDIORECORD_SUCCESS                         0
61#define AUDIORECORD_ERROR                           -1
62#define AUDIORECORD_ERROR_BAD_VALUE                 -2
63#define AUDIORECORD_ERROR_INVALID_OPERATION         -3
64#define AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT      -16
65#define AUDIORECORD_ERROR_SETUP_INVALIDCHANNELCOUNT -17
66#define AUDIORECORD_ERROR_SETUP_INVALIDFORMAT       -18
67#define AUDIORECORD_ERROR_SETUP_INVALIDSOURCE       -19
68#define AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED    -20
69
70jint android_media_translateRecorderErrorCode(int code) {
71    switch(code) {
72    case NO_ERROR:
73        return AUDIORECORD_SUCCESS;
74    case BAD_VALUE:
75        return AUDIORECORD_ERROR_BAD_VALUE;
76    case INVALID_OPERATION:
77        return AUDIORECORD_ERROR_INVALID_OPERATION;
78    default:
79        return AUDIORECORD_ERROR;
80    }
81}
82
83
84// ----------------------------------------------------------------------------
85static void recorderCallback(int event, void* user, void *info) {
86    if (event == AudioRecord::EVENT_MORE_DATA) {
87        // set size to 0 to signal we're not using the callback to read more data
88        AudioRecord::Buffer* pBuff = (AudioRecord::Buffer*)info;
89        pBuff->size = 0;
90
91    } else if (event == AudioRecord::EVENT_MARKER) {
92        audiorecord_callback_cookie *callbackInfo = (audiorecord_callback_cookie *)user;
93        JNIEnv *env = AndroidRuntime::getJNIEnv();
94        if (user && env) {
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
105    } else if (event == AudioRecord::EVENT_NEW_POS) {
106        audiorecord_callback_cookie *callbackInfo = (audiorecord_callback_cookie *)user;
107        JNIEnv *env = AndroidRuntime::getJNIEnv();
108        if (user && env) {
109            env->CallStaticVoidMethod(
110                callbackInfo->audioRecord_class,
111                javaAudioRecordFields.postNativeEventInJava,
112                callbackInfo->audioRecord_ref, event, 0,0, NULL);
113            if (env->ExceptionCheck()) {
114                env->ExceptionDescribe();
115                env->ExceptionClear();
116            }
117        }
118    }
119}
120
121
122// ----------------------------------------------------------------------------
123static int
124android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject weak_this,
125        jint source, jint sampleRateInHertz, jint nbChannels,
126        jint audioFormat, jint buffSizeInBytes)
127{
128    //LOGV(">> Entering android_media_AudioRecord_setup");
129    //LOGV("sampleRate=%d, audioFormat=%d, nbChannels=%d, buffSizeInBytes=%d",
130    //     sampleRateInHertz, audioFormat, nbChannels,     buffSizeInBytes);
131
132    if ((nbChannels == 0) || (nbChannels > 2)) {
133        LOGE("Error creating AudioRecord: channel count is not 1 or 2.");
134        return AUDIORECORD_ERROR_SETUP_INVALIDCHANNELCOUNT;
135    }
136
137    // compare the format against the Java constants
138    if ((audioFormat != javaAudioRecordFields.PCM16)
139        && (audioFormat != javaAudioRecordFields.PCM8)) {
140        LOGE("Error creating AudioRecord: unsupported audio format.");
141        return AUDIORECORD_ERROR_SETUP_INVALIDFORMAT;
142    }
143
144    int bytesPerSample = audioFormat==javaAudioRecordFields.PCM16 ? 2 : 1;
145    int format = audioFormat==javaAudioRecordFields.PCM16 ?
146            AudioSystem::PCM_16_BIT : AudioSystem::PCM_8_BIT;
147
148    if (buffSizeInBytes == 0) {
149         LOGE("Error creating AudioRecord: frameCount is 0.");
150        return AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT;
151    }
152    int frameSize = nbChannels * bytesPerSample;
153    size_t frameCount = buffSizeInBytes / frameSize;
154
155    // convert and check input source value
156    // input_source values defined in AudioRecord.h are equal to
157    // JAVA MediaRecord.AudioSource values minus 1.
158    AudioRecord::input_source arSource = (AudioRecord::input_source)(source - 1);
159    if (arSource < AudioRecord::DEFAULT_INPUT ||
160        arSource >= AudioRecord::NUM_INPUT_SOURCES) {
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(arSource,
188        sampleRateInHertz,
189        format,        // word length, PCM
190        nbChannels,
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 void
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;
239    }
240
241    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_finalize(JNIEnv *env,  jobject thiz) {
263
264    // delete the AudioRecord object
265    AudioRecord *lpRecorder =
266            (AudioRecord *)env->GetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
267
268    if (lpRecorder) {
269        LOGV("About to delete lpRecorder: %x\n", (int)lpRecorder);
270        lpRecorder->stop();
271        delete lpRecorder;
272    }
273
274    // delete the callback information
275    audiorecord_callback_cookie *lpCookie = (audiorecord_callback_cookie *)env->GetIntField(
276        thiz, javaAudioRecordFields.nativeCallbackCookie);
277    if (lpCookie) {
278        LOGV("deleting lpCookie: %x\n", (int)lpCookie);
279        env->DeleteGlobalRef(lpCookie->audioRecord_class);
280        env->DeleteGlobalRef(lpCookie->audioRecord_ref);
281        delete lpCookie;
282    }
283
284}
285
286
287// ----------------------------------------------------------------------------
288static void android_media_AudioRecord_release(JNIEnv *env,  jobject thiz) {
289
290    // do everything a call to finalize would
291    android_media_AudioRecord_finalize(env, thiz);
292    // + reset the native resources in the Java object so any attempt to access
293    // them after a call to release fails.
294    env->SetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj, 0);
295    env->SetIntField(thiz, javaAudioRecordFields.nativeCallbackCookie, 0);
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    recordBuff = (jbyte *)env->GetPrimitiveArrayCritical(javaAudioData, NULL);
321
322    if (recordBuff == NULL) {
323        LOGE("Error retrieving destination for recorded audio data, can't record");
324        return 0;
325    }
326
327    // read the new audio data from the native AudioRecord object
328    ssize_t recorderBuffSize = lpRecorder->frameCount()*lpRecorder->frameSize();
329    ssize_t readSize = lpRecorder->read(recordBuff + offsetInBytes,
330                                        sizeInBytes > (jint)recorderBuffSize ?
331                                            (jint)recorderBuffSize : sizeInBytes );
332    env->ReleasePrimitiveArrayCritical(javaAudioData, recordBuff, 0);
333
334    return (jint) readSize;
335}
336
337// ----------------------------------------------------------------------------
338static jint android_media_AudioRecord_readInShortArray(JNIEnv *env,  jobject thiz,
339                                                        jshortArray javaAudioData,
340                                                        jint offsetInShorts, jint sizeInShorts) {
341
342    return (android_media_AudioRecord_readInByteArray(env, thiz,
343                                                        (jbyteArray) javaAudioData,
344                                                        offsetInShorts*2, sizeInShorts*2)
345            / 2);
346}
347
348// ----------------------------------------------------------------------------
349static jint android_media_AudioRecord_readInDirectBuffer(JNIEnv *env,  jobject thiz,
350                                                  jobject jBuffer, jint sizeInBytes) {
351    AudioRecord *lpRecorder = NULL;
352    //LOGV("Entering android_media_AudioRecord_readInBuffer");
353
354    // get the audio recorder from which we'll read new audio samples
355    lpRecorder =
356        (AudioRecord *)env->GetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
357    if(lpRecorder==NULL)
358        return 0;
359
360    // direct buffer and direct access supported?
361    long capacity = env->GetDirectBufferCapacity(jBuffer);
362    if(capacity == -1) {
363        // buffer direct access is not supported
364        LOGE("Buffer direct access is not supported, can't record");
365        return 0;
366    }
367    //LOGV("capacity = %ld", capacity);
368    jbyte* nativeFromJavaBuf = (jbyte*) env->GetDirectBufferAddress(jBuffer);
369    if(nativeFromJavaBuf==NULL) {
370        LOGE("Buffer direct access is not supported, can't record");
371        return 0;
372    }
373
374    // read new data from the recorder
375    return (jint) lpRecorder->read(nativeFromJavaBuf,
376                                   capacity < sizeInBytes ? capacity : sizeInBytes);
377}
378
379
380// ----------------------------------------------------------------------------
381static jint android_media_AudioRecord_set_marker_pos(JNIEnv *env,  jobject thiz,
382        jint markerPos) {
383
384    AudioRecord *lpRecorder = (AudioRecord *)env->GetIntField(
385                thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
386
387    if (lpRecorder) {
388        return
389            android_media_translateRecorderErrorCode( lpRecorder->setMarkerPosition(markerPos) );
390    } else {
391        jniThrowException(env, "java/lang/IllegalStateException",
392            "Unable to retrieve AudioRecord pointer for setMarkerPosition()");
393        return AUDIORECORD_ERROR;
394    }
395}
396
397
398// ----------------------------------------------------------------------------
399static jint android_media_AudioRecord_get_marker_pos(JNIEnv *env,  jobject thiz) {
400
401    AudioRecord *lpRecorder = (AudioRecord *)env->GetIntField(
402                thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
403    uint32_t markerPos = 0;
404
405    if (lpRecorder) {
406        lpRecorder->getMarkerPosition(&markerPos);
407        return (jint)markerPos;
408    } else {
409        jniThrowException(env, "java/lang/IllegalStateException",
410            "Unable to retrieve AudioRecord pointer for getMarkerPosition()");
411        return AUDIORECORD_ERROR;
412    }
413}
414
415
416// ----------------------------------------------------------------------------
417static jint android_media_AudioRecord_set_pos_update_period(JNIEnv *env,  jobject thiz,
418        jint period) {
419
420    AudioRecord *lpRecorder = (AudioRecord *)env->GetIntField(
421                thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
422
423    if (lpRecorder) {
424        return
425            android_media_translateRecorderErrorCode( lpRecorder->setPositionUpdatePeriod(period) );
426    } else {
427        jniThrowException(env, "java/lang/IllegalStateException",
428            "Unable to retrieve AudioRecord pointer for setPositionUpdatePeriod()");
429        return AUDIORECORD_ERROR;
430    }
431}
432
433
434// ----------------------------------------------------------------------------
435static jint android_media_AudioRecord_get_pos_update_period(JNIEnv *env,  jobject thiz) {
436
437    AudioRecord *lpRecorder = (AudioRecord *)env->GetIntField(
438                thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
439    uint32_t period = 0;
440
441    if (lpRecorder) {
442        lpRecorder->getPositionUpdatePeriod(&period);
443        return (jint)period;
444    } else {
445        jniThrowException(env, "java/lang/IllegalStateException",
446            "Unable to retrieve AudioRecord pointer for getPositionUpdatePeriod()");
447        return AUDIORECORD_ERROR;
448    }
449}
450
451
452// ----------------------------------------------------------------------------
453// returns the minimum required size for the successful creation of an AudioRecord instance.
454// returns 0 if the parameter combination is not supported.
455// return -1 if there was an error querying the buffer size.
456static jint android_media_AudioRecord_get_min_buff_size(JNIEnv *env,  jobject thiz,
457    jint sampleRateInHertz, jint nbChannels, jint audioFormat) {
458
459    size_t inputBuffSize = 0;
460    LOGV(">> android_media_AudioRecord_get_min_buff_size(%d, %d, %d)", sampleRateInHertz, nbChannels, audioFormat);
461
462    status_t result = AudioSystem::getInputBufferSize(
463                        sampleRateInHertz,
464                        (audioFormat == javaAudioRecordFields.PCM16 ?
465                            AudioSystem::PCM_16_BIT : AudioSystem::PCM_8_BIT),
466                        nbChannels, &inputBuffSize);
467    switch(result) {
468    case(NO_ERROR):
469        if(inputBuffSize == 0) {
470            LOGV("Recording parameters are not supported: %dHz, %d channel(s), (java) format %d",
471                sampleRateInHertz, nbChannels, audioFormat);
472            return 0;
473        } else {
474            // the minimum buffer size is twice the hardware input buffer size
475            return 2*inputBuffSize;
476        }
477        break;
478    case(PERMISSION_DENIED):
479    default:
480        return -1;
481    }
482}
483
484
485// ----------------------------------------------------------------------------
486// ----------------------------------------------------------------------------
487static JNINativeMethod gMethods[] = {
488    // name,               signature,  funcPtr
489    {"native_start",         "()V",    (void *)android_media_AudioRecord_start},
490    {"native_stop",          "()V",    (void *)android_media_AudioRecord_stop},
491    {"native_setup",         "(Ljava/lang/Object;IIIII)I",
492                                       (void *)android_media_AudioRecord_setup},
493    {"native_finalize",      "()V",    (void *)android_media_AudioRecord_finalize},
494    {"native_release",       "()V",    (void *)android_media_AudioRecord_release},
495    {"native_read_in_byte_array",
496                             "([BII)I", (void *)android_media_AudioRecord_readInByteArray},
497    {"native_read_in_short_array",
498                             "([SII)I", (void *)android_media_AudioRecord_readInShortArray},
499    {"native_read_in_direct_buffer","(Ljava/lang/Object;I)I",
500                                       (void *)android_media_AudioRecord_readInDirectBuffer},
501    {"native_set_marker_pos","(I)I",   (void *)android_media_AudioRecord_set_marker_pos},
502    {"native_get_marker_pos","()I",    (void *)android_media_AudioRecord_get_marker_pos},
503    {"native_set_pos_update_period",
504                             "(I)I",   (void *)android_media_AudioRecord_set_pos_update_period},
505    {"native_get_pos_update_period",
506                             "()I",    (void *)android_media_AudioRecord_get_pos_update_period},
507    {"native_get_min_buff_size",
508                             "(III)I",   (void *)android_media_AudioRecord_get_min_buff_size},
509};
510
511// field names found in android/media/AudioRecord.java
512#define JAVA_POSTEVENT_CALLBACK_NAME  "postEventFromNative"
513#define JAVA_CONST_PCM16_NAME         "ENCODING_PCM_16BIT"
514#define JAVA_CONST_PCM8_NAME          "ENCODING_PCM_8BIT"
515#define JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME  "mNativeRecorderInJavaObj"
516#define JAVA_NATIVECALLBACKINFO_FIELD_NAME       "mNativeCallbackCookie"
517
518#define JAVA_AUDIOFORMAT_CLASS_NAME "android/media/AudioFormat"
519
520// ----------------------------------------------------------------------------
521
522extern bool android_media_getIntConstantFromClass(JNIEnv* pEnv,
523                jclass theClass, const char* className, const char* constName, int* constVal);
524
525// ----------------------------------------------------------------------------
526int register_android_media_AudioRecord(JNIEnv *env)
527{
528    javaAudioRecordFields.audioRecordClass = NULL;
529    javaAudioRecordFields.postNativeEventInJava = NULL;
530    javaAudioRecordFields.nativeRecorderInJavaObj = NULL;
531    javaAudioRecordFields.nativeCallbackCookie = NULL;
532
533
534    // Get the AudioRecord class
535    javaAudioRecordFields.audioRecordClass = env->FindClass(kClassPathName);
536    if (javaAudioRecordFields.audioRecordClass == NULL) {
537        LOGE("Can't find %s", kClassPathName);
538        return -1;
539    }
540
541    // Get the postEvent method
542    javaAudioRecordFields.postNativeEventInJava = env->GetStaticMethodID(
543            javaAudioRecordFields.audioRecordClass,
544            JAVA_POSTEVENT_CALLBACK_NAME, "(Ljava/lang/Object;IIILjava/lang/Object;)V");
545    if (javaAudioRecordFields.postNativeEventInJava == NULL) {
546        LOGE("Can't find AudioRecord.%s", JAVA_POSTEVENT_CALLBACK_NAME);
547        return -1;
548    }
549
550    // Get the variables
551    //    mNativeRecorderInJavaObj
552    javaAudioRecordFields.nativeRecorderInJavaObj =
553        env->GetFieldID(javaAudioRecordFields.audioRecordClass,
554                        JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME, "I");
555    if (javaAudioRecordFields.nativeRecorderInJavaObj == NULL) {
556        LOGE("Can't find AudioRecord.%s", JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME);
557        return -1;
558    }
559    //     mNativeCallbackCookie
560    javaAudioRecordFields.nativeCallbackCookie = env->GetFieldID(
561            javaAudioRecordFields.audioRecordClass,
562            JAVA_NATIVECALLBACKINFO_FIELD_NAME, "I");
563    if (javaAudioRecordFields.nativeCallbackCookie == NULL) {
564        LOGE("Can't find AudioRecord.%s", JAVA_NATIVECALLBACKINFO_FIELD_NAME);
565        return -1;
566    }
567
568    // Get the format constants from the AudioFormat class
569    jclass audioFormatClass = NULL;
570    audioFormatClass = env->FindClass(JAVA_AUDIOFORMAT_CLASS_NAME);
571    if (audioFormatClass == NULL) {
572        LOGE("Can't find %s", JAVA_AUDIOFORMAT_CLASS_NAME);
573        return -1;
574    }
575    if ( !android_media_getIntConstantFromClass(env, audioFormatClass,
576                JAVA_AUDIOFORMAT_CLASS_NAME,
577                JAVA_CONST_PCM16_NAME, &(javaAudioRecordFields.PCM16))
578           || !android_media_getIntConstantFromClass(env, audioFormatClass,
579                JAVA_AUDIOFORMAT_CLASS_NAME,
580                JAVA_CONST_PCM8_NAME, &(javaAudioRecordFields.PCM8)) ) {
581        // error log performed in getIntConstantFromClass()
582        return -1;
583    }
584
585    return AndroidRuntime::registerNativeMethods(env,
586            kClassPathName, gMethods, NELEM(gMethods));
587}
588
589// ----------------------------------------------------------------------------
590