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