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 "core_jni_helpers.h"
25
26#include <utils/Log.h>
27#include <media/AudioRecord.h>
28
29#include <ScopedUtfChars.h>
30
31#include "android_media_AudioFormat.h"
32#include "android_media_AudioErrors.h"
33#include "android_media_DeviceCallback.h"
34
35// ----------------------------------------------------------------------------
36
37using namespace android;
38
39// ----------------------------------------------------------------------------
40static const char* const kClassPathName = "android/media/AudioRecord";
41static const char* const kAudioAttributesClassPathName = "android/media/AudioAttributes";
42
43struct audio_record_fields_t {
44    // these fields provide access from C++ to the...
45    jmethodID postNativeEventInJava; //... event post callback method
46    jfieldID  nativeRecorderInJavaObj; // provides access to the C++ AudioRecord object
47    jfieldID  nativeCallbackCookie;    // provides access to the AudioRecord callback data
48    jfieldID  nativeDeviceCallback;    // provides access to the JNIDeviceCallback instance
49};
50struct audio_attributes_fields_t {
51    jfieldID  fieldRecSource;    // AudioAttributes.mSource
52    jfieldID  fieldFlags;        // AudioAttributes.mFlags
53    jfieldID  fieldFormattedTags;// AudioAttributes.mFormattedTags
54};
55static audio_attributes_fields_t javaAudioAttrFields;
56static audio_record_fields_t     javaAudioRecordFields;
57
58struct audiorecord_callback_cookie {
59    jclass      audioRecord_class;
60    jobject     audioRecord_ref;
61    bool        busy;
62    Condition   cond;
63};
64
65static Mutex sLock;
66static SortedVector <audiorecord_callback_cookie *> sAudioRecordCallBackCookies;
67
68// ----------------------------------------------------------------------------
69
70#define AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT      -16
71#define AUDIORECORD_ERROR_SETUP_INVALIDCHANNELMASK -17
72#define AUDIORECORD_ERROR_SETUP_INVALIDFORMAT       -18
73#define AUDIORECORD_ERROR_SETUP_INVALIDSOURCE       -19
74#define AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED    -20
75
76// ----------------------------------------------------------------------------
77static void recorderCallback(int event, void* user, void *info) {
78
79    audiorecord_callback_cookie *callbackInfo = (audiorecord_callback_cookie *)user;
80    {
81        Mutex::Autolock l(sLock);
82        if (sAudioRecordCallBackCookies.indexOf(callbackInfo) < 0) {
83            return;
84        }
85        callbackInfo->busy = true;
86    }
87
88    switch (event) {
89    case AudioRecord::EVENT_MARKER: {
90        JNIEnv *env = AndroidRuntime::getJNIEnv();
91        if (user != NULL && env != NULL) {
92            env->CallStaticVoidMethod(
93                callbackInfo->audioRecord_class,
94                javaAudioRecordFields.postNativeEventInJava,
95                callbackInfo->audioRecord_ref, event, 0,0, NULL);
96            if (env->ExceptionCheck()) {
97                env->ExceptionDescribe();
98                env->ExceptionClear();
99            }
100        }
101        } break;
102
103    case AudioRecord::EVENT_NEW_POS: {
104        JNIEnv *env = AndroidRuntime::getJNIEnv();
105        if (user != NULL && env != NULL) {
106            env->CallStaticVoidMethod(
107                callbackInfo->audioRecord_class,
108                javaAudioRecordFields.postNativeEventInJava,
109                callbackInfo->audioRecord_ref, event, 0,0, NULL);
110            if (env->ExceptionCheck()) {
111                env->ExceptionDescribe();
112                env->ExceptionClear();
113            }
114        }
115        } break;
116    }
117
118    {
119        Mutex::Autolock l(sLock);
120        callbackInfo->busy = false;
121        callbackInfo->cond.broadcast();
122    }
123}
124
125static sp<JNIDeviceCallback> getJniDeviceCallback(JNIEnv* env, jobject thiz)
126{
127    Mutex::Autolock l(sLock);
128    JNIDeviceCallback* const cb =
129            (JNIDeviceCallback*)env->GetLongField(thiz,
130                                                  javaAudioRecordFields.nativeDeviceCallback);
131    return sp<JNIDeviceCallback>(cb);
132}
133
134static sp<JNIDeviceCallback> setJniDeviceCallback(JNIEnv* env,
135                                                  jobject thiz,
136                                                  const sp<JNIDeviceCallback>& cb)
137{
138    Mutex::Autolock l(sLock);
139    sp<JNIDeviceCallback> old =
140            (JNIDeviceCallback*)env->GetLongField(thiz,
141                                                  javaAudioRecordFields.nativeDeviceCallback);
142    if (cb.get()) {
143        cb->incStrong((void*)setJniDeviceCallback);
144    }
145    if (old != 0) {
146        old->decStrong((void*)setJniDeviceCallback);
147    }
148    env->SetLongField(thiz, javaAudioRecordFields.nativeDeviceCallback, (jlong)cb.get());
149    return old;
150}
151
152// ----------------------------------------------------------------------------
153static sp<AudioRecord> getAudioRecord(JNIEnv* env, jobject thiz)
154{
155    Mutex::Autolock l(sLock);
156    AudioRecord* const ar =
157            (AudioRecord*)env->GetLongField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
158    return sp<AudioRecord>(ar);
159}
160
161static sp<AudioRecord> setAudioRecord(JNIEnv* env, jobject thiz, const sp<AudioRecord>& ar)
162{
163    Mutex::Autolock l(sLock);
164    sp<AudioRecord> old =
165            (AudioRecord*)env->GetLongField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
166    if (ar.get()) {
167        ar->incStrong((void*)setAudioRecord);
168    }
169    if (old != 0) {
170        old->decStrong((void*)setAudioRecord);
171    }
172    env->SetLongField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj, (jlong)ar.get());
173    return old;
174}
175
176// ----------------------------------------------------------------------------
177static jint
178android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject weak_this,
179        jobject jaa, jint sampleRateInHertz, jint channelMask, jint channelIndexMask,
180        jint audioFormat, jint buffSizeInBytes, jintArray jSession, jstring opPackageName)
181{
182    //ALOGV(">> Entering android_media_AudioRecord_setup");
183    //ALOGV("sampleRate=%d, audioFormat=%d, channel mask=%x, buffSizeInBytes=%d",
184    //     sampleRateInHertz, audioFormat, channelMask, buffSizeInBytes);
185
186    if (jaa == 0) {
187        ALOGE("Error creating AudioRecord: invalid audio attributes");
188        return (jint) AUDIO_JAVA_ERROR;
189    }
190
191    // channel index mask takes priority over channel position masks.
192    if (channelIndexMask) {
193        // Java channel index masks need the representation bits set.
194        channelMask = audio_channel_mask_from_representation_and_bits(
195                AUDIO_CHANNEL_REPRESENTATION_INDEX,
196                channelIndexMask);
197    }
198    // Java channel position masks map directly to the native definition
199
200    if (!audio_is_input_channel(channelMask)) {
201        ALOGE("Error creating AudioRecord: channel mask %#x is not valid.", channelMask);
202        return (jint) AUDIORECORD_ERROR_SETUP_INVALIDCHANNELMASK;
203    }
204    uint32_t channelCount = audio_channel_count_from_in_mask(channelMask);
205
206    // compare the format against the Java constants
207    audio_format_t format = audioFormatToNative(audioFormat);
208    if (format == AUDIO_FORMAT_INVALID) {
209        ALOGE("Error creating AudioRecord: unsupported audio format %d.", audioFormat);
210        return (jint) AUDIORECORD_ERROR_SETUP_INVALIDFORMAT;
211    }
212
213    size_t bytesPerSample = audio_bytes_per_sample(format);
214
215    if (buffSizeInBytes == 0) {
216         ALOGE("Error creating AudioRecord: frameCount is 0.");
217        return (jint) AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT;
218    }
219    size_t frameSize = channelCount * bytesPerSample;
220    size_t frameCount = buffSizeInBytes / frameSize;
221
222    jclass clazz = env->GetObjectClass(thiz);
223    if (clazz == NULL) {
224        ALOGE("Can't find %s when setting up callback.", kClassPathName);
225        return (jint) AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED;
226    }
227
228    if (jSession == NULL) {
229        ALOGE("Error creating AudioRecord: invalid session ID pointer");
230        return (jint) AUDIO_JAVA_ERROR;
231    }
232
233    jint* nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
234    if (nSession == NULL) {
235        ALOGE("Error creating AudioRecord: Error retrieving session id pointer");
236        return (jint) AUDIO_JAVA_ERROR;
237    }
238    int sessionId = nSession[0];
239    env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
240    nSession = NULL;
241
242    ScopedUtfChars opPackageNameStr(env, opPackageName);
243
244    // create an uninitialized AudioRecord object
245    sp<AudioRecord> lpRecorder = new AudioRecord(String16(opPackageNameStr.c_str()));
246
247    audio_attributes_t *paa = NULL;
248    // read the AudioAttributes values
249    paa = (audio_attributes_t *) calloc(1, sizeof(audio_attributes_t));
250    const jstring jtags =
251            (jstring) env->GetObjectField(jaa, javaAudioAttrFields.fieldFormattedTags);
252    const char* tags = env->GetStringUTFChars(jtags, NULL);
253    // copying array size -1, char array for tags was calloc'd, no need to NULL-terminate it
254    strncpy(paa->tags, tags, AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1);
255    env->ReleaseStringUTFChars(jtags, tags);
256    paa->source = (audio_source_t) env->GetIntField(jaa, javaAudioAttrFields.fieldRecSource);
257    paa->flags = (audio_flags_mask_t)env->GetIntField(jaa, javaAudioAttrFields.fieldFlags);
258    ALOGV("AudioRecord_setup for source=%d tags=%s flags=%08x", paa->source, paa->tags, paa->flags);
259
260    audio_input_flags_t flags = AUDIO_INPUT_FLAG_NONE;
261    if (paa->flags & AUDIO_FLAG_HW_HOTWORD) {
262        flags = AUDIO_INPUT_FLAG_HW_HOTWORD;
263    }
264    // create the callback information:
265    // this data will be passed with every AudioRecord callback
266    audiorecord_callback_cookie *lpCallbackData = new audiorecord_callback_cookie;
267    lpCallbackData->audioRecord_class = (jclass)env->NewGlobalRef(clazz);
268    // we use a weak reference so the AudioRecord object can be garbage collected.
269    lpCallbackData->audioRecord_ref = env->NewGlobalRef(weak_this);
270    lpCallbackData->busy = false;
271
272    const status_t status = lpRecorder->set(paa->source,
273        sampleRateInHertz,
274        format,        // word length, PCM
275        channelMask,
276        frameCount,
277        recorderCallback,// callback_t
278        lpCallbackData,// void* user
279        0,             // notificationFrames,
280        true,          // threadCanCallJava
281        sessionId,
282        AudioRecord::TRANSFER_DEFAULT,
283        flags,
284        -1, -1,        // default uid, pid
285        paa);
286
287    if (status != NO_ERROR) {
288        ALOGE("Error creating AudioRecord instance: initialization check failed with status %d.",
289                status);
290        goto native_init_failure;
291    }
292
293    nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
294    if (nSession == NULL) {
295        ALOGE("Error creating AudioRecord: Error retrieving session id pointer");
296        goto native_init_failure;
297    }
298    // read the audio session ID back from AudioRecord in case a new session was created during set()
299    nSession[0] = lpRecorder->getSessionId();
300    env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
301    nSession = NULL;
302
303    {   // scope for the lock
304        Mutex::Autolock l(sLock);
305        sAudioRecordCallBackCookies.add(lpCallbackData);
306    }
307    // save our newly created C++ AudioRecord in the "nativeRecorderInJavaObj" field
308    // of the Java object
309    setAudioRecord(env, thiz, lpRecorder);
310
311    // save our newly created callback information in the "nativeCallbackCookie" field
312    // of the Java object (in mNativeCallbackCookie) so we can free the memory in finalize()
313    env->SetLongField(thiz, javaAudioRecordFields.nativeCallbackCookie, (jlong)lpCallbackData);
314
315    return (jint) AUDIO_JAVA_SUCCESS;
316
317    // failure:
318native_init_failure:
319    env->DeleteGlobalRef(lpCallbackData->audioRecord_class);
320    env->DeleteGlobalRef(lpCallbackData->audioRecord_ref);
321    delete lpCallbackData;
322    env->SetLongField(thiz, javaAudioRecordFields.nativeCallbackCookie, 0);
323
324    // lpRecorder goes out of scope, so reference count drops to zero
325    return (jint) AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED;
326}
327
328
329
330// ----------------------------------------------------------------------------
331static jint
332android_media_AudioRecord_start(JNIEnv *env, jobject thiz, jint event, jint triggerSession)
333{
334    sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
335    if (lpRecorder == NULL ) {
336        jniThrowException(env, "java/lang/IllegalStateException", NULL);
337        return (jint) AUDIO_JAVA_ERROR;
338    }
339
340    return nativeToJavaStatus(
341            lpRecorder->start((AudioSystem::sync_event_t)event, triggerSession));
342}
343
344
345// ----------------------------------------------------------------------------
346static void
347android_media_AudioRecord_stop(JNIEnv *env, jobject thiz)
348{
349    sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
350    if (lpRecorder == NULL ) {
351        jniThrowException(env, "java/lang/IllegalStateException", NULL);
352        return;
353    }
354
355    lpRecorder->stop();
356    //ALOGV("Called lpRecorder->stop()");
357}
358
359
360// ----------------------------------------------------------------------------
361
362#define CALLBACK_COND_WAIT_TIMEOUT_MS 1000
363static void android_media_AudioRecord_release(JNIEnv *env,  jobject thiz) {
364    sp<AudioRecord> lpRecorder = setAudioRecord(env, thiz, 0);
365    if (lpRecorder == NULL) {
366        return;
367    }
368    ALOGV("About to delete lpRecorder: %p", lpRecorder.get());
369    lpRecorder->stop();
370
371    audiorecord_callback_cookie *lpCookie = (audiorecord_callback_cookie *)env->GetLongField(
372        thiz, javaAudioRecordFields.nativeCallbackCookie);
373
374    // reset the native resources in the Java object so any attempt to access
375    // them after a call to release fails.
376    env->SetLongField(thiz, javaAudioRecordFields.nativeCallbackCookie, 0);
377
378    // delete the callback information
379    if (lpCookie) {
380        Mutex::Autolock l(sLock);
381        ALOGV("deleting lpCookie: %p", lpCookie);
382        while (lpCookie->busy) {
383            if (lpCookie->cond.waitRelative(sLock,
384                                            milliseconds(CALLBACK_COND_WAIT_TIMEOUT_MS)) !=
385                                                    NO_ERROR) {
386                break;
387            }
388        }
389        sAudioRecordCallBackCookies.remove(lpCookie);
390        env->DeleteGlobalRef(lpCookie->audioRecord_class);
391        env->DeleteGlobalRef(lpCookie->audioRecord_ref);
392        delete lpCookie;
393    }
394}
395
396
397// ----------------------------------------------------------------------------
398static void android_media_AudioRecord_finalize(JNIEnv *env,  jobject thiz) {
399    android_media_AudioRecord_release(env, thiz);
400}
401
402// overloaded JNI array helper functions
403static inline
404jbyte *envGetArrayElements(JNIEnv *env, jbyteArray array, jboolean *isCopy) {
405    return env->GetByteArrayElements(array, isCopy);
406}
407
408static inline
409void envReleaseArrayElements(JNIEnv *env, jbyteArray array, jbyte *elems, jint mode) {
410    env->ReleaseByteArrayElements(array, elems, mode);
411}
412
413static inline
414jshort *envGetArrayElements(JNIEnv *env, jshortArray array, jboolean *isCopy) {
415    return env->GetShortArrayElements(array, isCopy);
416}
417
418static inline
419void envReleaseArrayElements(JNIEnv *env, jshortArray array, jshort *elems, jint mode) {
420    env->ReleaseShortArrayElements(array, elems, mode);
421}
422
423static inline
424jfloat *envGetArrayElements(JNIEnv *env, jfloatArray array, jboolean *isCopy) {
425    return env->GetFloatArrayElements(array, isCopy);
426}
427
428static inline
429void envReleaseArrayElements(JNIEnv *env, jfloatArray array, jfloat *elems, jint mode) {
430    env->ReleaseFloatArrayElements(array, elems, mode);
431}
432
433static inline
434jint interpretReadSizeError(ssize_t readSize) {
435    ALOGE_IF(readSize != WOULD_BLOCK, "Error %zd during AudioRecord native read", readSize);
436    switch (readSize) {
437    case WOULD_BLOCK:
438        return (jint)0;
439    case BAD_VALUE:
440        return (jint)AUDIO_JAVA_BAD_VALUE;
441    default:
442        // may be possible for other errors such as
443        // NO_INIT to happen if restoreRecord_l fails.
444    case INVALID_OPERATION:
445        return (jint)AUDIO_JAVA_INVALID_OPERATION;
446    }
447}
448
449template <typename T>
450static jint android_media_AudioRecord_readInArray(JNIEnv *env,  jobject thiz,
451                                                  T javaAudioData,
452                                                  jint offsetInSamples, jint sizeInSamples,
453                                                  jboolean isReadBlocking) {
454    // get the audio recorder from which we'll read new audio samples
455    sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
456    if (lpRecorder == NULL) {
457        ALOGE("Unable to retrieve AudioRecord object");
458        return (jint)AUDIO_JAVA_INVALID_OPERATION;
459    }
460
461    if (javaAudioData == NULL) {
462        ALOGE("Invalid Java array to store recorded audio");
463        return (jint)AUDIO_JAVA_BAD_VALUE;
464    }
465
466    // NOTE: We may use GetPrimitiveArrayCritical() when the JNI implementation changes in such
467    // a way that it becomes much more efficient. When doing so, we will have to prevent the
468    // AudioSystem callback to be called while in critical section (in case of media server
469    // process crash for instance)
470
471    // get the pointer to where we'll record the audio
472    auto *recordBuff = envGetArrayElements(env, javaAudioData, NULL);
473    if (recordBuff == NULL) {
474        ALOGE("Error retrieving destination for recorded audio data");
475        return (jint)AUDIO_JAVA_BAD_VALUE;
476    }
477
478    // read the new audio data from the native AudioRecord object
479    const size_t sizeInBytes = sizeInSamples * sizeof(*recordBuff);
480    ssize_t readSize = lpRecorder->read(
481            recordBuff + offsetInSamples, sizeInBytes, isReadBlocking == JNI_TRUE /* blocking */);
482
483    envReleaseArrayElements(env, javaAudioData, recordBuff, 0);
484
485    if (readSize < 0) {
486        return interpretReadSizeError(readSize);
487    }
488    return (jint)(readSize / sizeof(*recordBuff));
489}
490
491// ----------------------------------------------------------------------------
492static jint android_media_AudioRecord_readInDirectBuffer(JNIEnv *env,  jobject thiz,
493                                                         jobject jBuffer, jint sizeInBytes,
494                                                         jboolean isReadBlocking) {
495    // get the audio recorder from which we'll read new audio samples
496    sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
497    if (lpRecorder==NULL)
498        return (jint)AUDIO_JAVA_INVALID_OPERATION;
499
500    // direct buffer and direct access supported?
501    long capacity = env->GetDirectBufferCapacity(jBuffer);
502    if (capacity == -1) {
503        // buffer direct access is not supported
504        ALOGE("Buffer direct access is not supported, can't record");
505        return (jint)AUDIO_JAVA_BAD_VALUE;
506    }
507    //ALOGV("capacity = %ld", capacity);
508    jbyte* nativeFromJavaBuf = (jbyte*) env->GetDirectBufferAddress(jBuffer);
509    if (nativeFromJavaBuf==NULL) {
510        ALOGE("Buffer direct access is not supported, can't record");
511        return (jint)AUDIO_JAVA_BAD_VALUE;
512    }
513
514    // read new data from the recorder
515    ssize_t readSize = lpRecorder->read(nativeFromJavaBuf,
516                                        capacity < sizeInBytes ? capacity : sizeInBytes,
517                                        isReadBlocking == JNI_TRUE /* blocking */);
518    if (readSize < 0) {
519        return interpretReadSizeError(readSize);
520    }
521    return (jint)readSize;
522}
523
524// ----------------------------------------------------------------------------
525static jint android_media_AudioRecord_get_buffer_size_in_frames(JNIEnv *env,  jobject thiz) {
526    sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
527    if (lpRecorder == NULL) {
528        jniThrowException(env, "java/lang/IllegalStateException",
529            "Unable to retrieve AudioRecord pointer for frameCount()");
530        return (jint)AUDIO_JAVA_ERROR;
531    }
532    return lpRecorder->frameCount();
533}
534
535// ----------------------------------------------------------------------------
536static jint android_media_AudioRecord_set_marker_pos(JNIEnv *env,  jobject thiz,
537        jint markerPos) {
538
539    sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
540    if (lpRecorder == NULL) {
541        jniThrowException(env, "java/lang/IllegalStateException",
542            "Unable to retrieve AudioRecord pointer for setMarkerPosition()");
543        return (jint)AUDIO_JAVA_ERROR;
544    }
545    return nativeToJavaStatus( lpRecorder->setMarkerPosition(markerPos) );
546}
547
548
549// ----------------------------------------------------------------------------
550static jint android_media_AudioRecord_get_marker_pos(JNIEnv *env,  jobject thiz) {
551
552    sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
553    uint32_t markerPos = 0;
554
555    if (lpRecorder == NULL) {
556        jniThrowException(env, "java/lang/IllegalStateException",
557            "Unable to retrieve AudioRecord pointer for getMarkerPosition()");
558        return (jint)AUDIO_JAVA_ERROR;
559    }
560    lpRecorder->getMarkerPosition(&markerPos);
561    return (jint)markerPos;
562}
563
564
565// ----------------------------------------------------------------------------
566static jint android_media_AudioRecord_set_pos_update_period(JNIEnv *env,  jobject thiz,
567        jint period) {
568
569    sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
570
571    if (lpRecorder == NULL) {
572        jniThrowException(env, "java/lang/IllegalStateException",
573            "Unable to retrieve AudioRecord pointer for setPositionUpdatePeriod()");
574        return (jint)AUDIO_JAVA_ERROR;
575    }
576    return nativeToJavaStatus( lpRecorder->setPositionUpdatePeriod(period) );
577}
578
579
580// ----------------------------------------------------------------------------
581static jint android_media_AudioRecord_get_pos_update_period(JNIEnv *env,  jobject thiz) {
582
583    sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
584    uint32_t period = 0;
585
586    if (lpRecorder == NULL) {
587        jniThrowException(env, "java/lang/IllegalStateException",
588            "Unable to retrieve AudioRecord pointer for getPositionUpdatePeriod()");
589        return (jint)AUDIO_JAVA_ERROR;
590    }
591    lpRecorder->getPositionUpdatePeriod(&period);
592    return (jint)period;
593}
594
595
596// ----------------------------------------------------------------------------
597// returns the minimum required size for the successful creation of an AudioRecord instance.
598// returns 0 if the parameter combination is not supported.
599// return -1 if there was an error querying the buffer size.
600static jint android_media_AudioRecord_get_min_buff_size(JNIEnv *env,  jobject thiz,
601    jint sampleRateInHertz, jint channelCount, jint audioFormat) {
602
603    ALOGV(">> android_media_AudioRecord_get_min_buff_size(%d, %d, %d)",
604          sampleRateInHertz, channelCount, audioFormat);
605
606    size_t frameCount = 0;
607    audio_format_t format = audioFormatToNative(audioFormat);
608    status_t result = AudioRecord::getMinFrameCount(&frameCount,
609            sampleRateInHertz,
610            format,
611            audio_channel_in_mask_from_count(channelCount));
612
613    if (result == BAD_VALUE) {
614        return 0;
615    }
616    if (result != NO_ERROR) {
617        return -1;
618    }
619    return frameCount * channelCount * audio_bytes_per_sample(format);
620}
621
622static jboolean android_media_AudioRecord_setInputDevice(
623        JNIEnv *env,  jobject thiz, jint device_id) {
624
625    sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
626    if (lpRecorder == 0) {
627        return false;
628    }
629    return lpRecorder->setInputDevice(device_id) == NO_ERROR;
630}
631
632static jint android_media_AudioRecord_getRoutedDeviceId(
633                JNIEnv *env,  jobject thiz) {
634
635    sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
636    if (lpRecorder == 0) {
637        return 0;
638    }
639    return (jint)lpRecorder->getRoutedDeviceId();
640}
641
642static void android_media_AudioRecord_enableDeviceCallback(
643                JNIEnv *env,  jobject thiz) {
644
645    sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
646    if (lpRecorder == 0) {
647        return;
648    }
649    sp<JNIDeviceCallback> cb = getJniDeviceCallback(env, thiz);
650    if (cb != 0) {
651        return;
652    }
653    audiorecord_callback_cookie *cookie =
654            (audiorecord_callback_cookie *)env->GetLongField(thiz,
655                                                     javaAudioRecordFields.nativeCallbackCookie);
656    if (cookie == NULL) {
657        return;
658    }
659
660    cb = new JNIDeviceCallback(env, thiz, cookie->audioRecord_ref,
661                               javaAudioRecordFields.postNativeEventInJava);
662    status_t status = lpRecorder->addAudioDeviceCallback(cb);
663    if (status == NO_ERROR) {
664        setJniDeviceCallback(env, thiz, cb);
665    }
666}
667
668static void android_media_AudioRecord_disableDeviceCallback(
669                JNIEnv *env,  jobject thiz) {
670
671    sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
672    if (lpRecorder == 0) {
673        return;
674    }
675    sp<JNIDeviceCallback> cb = setJniDeviceCallback(env, thiz, 0);
676    if (cb != 0) {
677        lpRecorder->removeAudioDeviceCallback(cb);
678    }
679}
680
681
682
683// ----------------------------------------------------------------------------
684// ----------------------------------------------------------------------------
685static JNINativeMethod gMethods[] = {
686    // name,               signature,  funcPtr
687    {"native_start",         "(II)I",    (void *)android_media_AudioRecord_start},
688    {"native_stop",          "()V",    (void *)android_media_AudioRecord_stop},
689    {"native_setup",         "(Ljava/lang/Object;Ljava/lang/Object;IIIII[ILjava/lang/String;)I",
690                                       (void *)android_media_AudioRecord_setup},
691    {"native_finalize",      "()V",    (void *)android_media_AudioRecord_finalize},
692    {"native_release",       "()V",    (void *)android_media_AudioRecord_release},
693    {"native_read_in_byte_array",
694                             "([BIIZ)I",
695                                     (void *)android_media_AudioRecord_readInArray<jbyteArray>},
696    {"native_read_in_short_array",
697                             "([SIIZ)I",
698                                     (void *)android_media_AudioRecord_readInArray<jshortArray>},
699    {"native_read_in_float_array",
700                             "([FIIZ)I",
701                                     (void *)android_media_AudioRecord_readInArray<jfloatArray>},
702    {"native_read_in_direct_buffer","(Ljava/lang/Object;IZ)I",
703                                       (void *)android_media_AudioRecord_readInDirectBuffer},
704    {"native_get_buffer_size_in_frames",
705                             "()I", (void *)android_media_AudioRecord_get_buffer_size_in_frames},
706    {"native_set_marker_pos","(I)I",   (void *)android_media_AudioRecord_set_marker_pos},
707    {"native_get_marker_pos","()I",    (void *)android_media_AudioRecord_get_marker_pos},
708    {"native_set_pos_update_period",
709                             "(I)I",   (void *)android_media_AudioRecord_set_pos_update_period},
710    {"native_get_pos_update_period",
711                             "()I",    (void *)android_media_AudioRecord_get_pos_update_period},
712    {"native_get_min_buff_size",
713                             "(III)I",   (void *)android_media_AudioRecord_get_min_buff_size},
714    {"native_setInputDevice", "(I)Z", (void *)android_media_AudioRecord_setInputDevice},
715    {"native_getRoutedDeviceId", "()I", (void *)android_media_AudioRecord_getRoutedDeviceId},
716    {"native_enableDeviceCallback", "()V", (void *)android_media_AudioRecord_enableDeviceCallback},
717    {"native_disableDeviceCallback", "()V",
718                                        (void *)android_media_AudioRecord_disableDeviceCallback},
719};
720
721// field names found in android/media/AudioRecord.java
722#define JAVA_POSTEVENT_CALLBACK_NAME  "postEventFromNative"
723#define JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME  "mNativeRecorderInJavaObj"
724#define JAVA_NATIVECALLBACKINFO_FIELD_NAME       "mNativeCallbackCookie"
725#define JAVA_NATIVEDEVICECALLBACK_FIELD_NAME       "mNativeDeviceCallback"
726
727// ----------------------------------------------------------------------------
728int register_android_media_AudioRecord(JNIEnv *env)
729{
730    javaAudioRecordFields.postNativeEventInJava = NULL;
731    javaAudioRecordFields.nativeRecorderInJavaObj = NULL;
732    javaAudioRecordFields.nativeCallbackCookie = NULL;
733    javaAudioRecordFields.nativeDeviceCallback = NULL;
734
735
736    // Get the AudioRecord class
737    jclass audioRecordClass = FindClassOrDie(env, kClassPathName);
738    // Get the postEvent method
739    javaAudioRecordFields.postNativeEventInJava = GetStaticMethodIDOrDie(env,
740            audioRecordClass, JAVA_POSTEVENT_CALLBACK_NAME,
741            "(Ljava/lang/Object;IIILjava/lang/Object;)V");
742
743    // Get the variables
744    //    mNativeRecorderInJavaObj
745    javaAudioRecordFields.nativeRecorderInJavaObj = GetFieldIDOrDie(env,
746            audioRecordClass, JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME, "J");
747    //     mNativeCallbackCookie
748    javaAudioRecordFields.nativeCallbackCookie = GetFieldIDOrDie(env,
749            audioRecordClass, JAVA_NATIVECALLBACKINFO_FIELD_NAME, "J");
750
751    javaAudioRecordFields.nativeDeviceCallback = GetFieldIDOrDie(env,
752            audioRecordClass, JAVA_NATIVEDEVICECALLBACK_FIELD_NAME, "J");
753
754    // Get the AudioAttributes class and fields
755    jclass audioAttrClass = FindClassOrDie(env, kAudioAttributesClassPathName);
756    javaAudioAttrFields.fieldRecSource = GetFieldIDOrDie(env, audioAttrClass, "mSource", "I");
757    javaAudioAttrFields.fieldFlags = GetFieldIDOrDie(env, audioAttrClass, "mFlags", "I");
758    javaAudioAttrFields.fieldFormattedTags = GetFieldIDOrDie(env,
759            audioAttrClass, "mFormattedTags", "Ljava/lang/String;");
760
761    return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
762}
763
764// ----------------------------------------------------------------------------
765