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