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 
37 using namespace android;
38 
39 // ----------------------------------------------------------------------------
40 static const char* const kClassPathName = "android/media/AudioRecord";
41 static const char* const kAudioAttributesClassPathName = "android/media/AudioAttributes";
42 
43 struct 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 };
50 struct audio_attributes_fields_t {
51     jfieldID  fieldRecSource;    // AudioAttributes.mSource
52     jfieldID  fieldFlags;        // AudioAttributes.mFlags
53     jfieldID  fieldFormattedTags;// AudioAttributes.mFormattedTags
54 };
55 static audio_attributes_fields_t javaAudioAttrFields;
56 static audio_record_fields_t     javaAudioRecordFields;
57 static struct {
58     jfieldID  fieldFramePosition;     // AudioTimestamp.framePosition
59     jfieldID  fieldNanoTime;          // AudioTimestamp.nanoTime
60 } javaAudioTimestampFields;
61 
62 struct audiorecord_callback_cookie {
63     jclass      audioRecord_class;
64     jobject     audioRecord_ref;
65     bool        busy;
66     Condition   cond;
67 };
68 
69 static Mutex sLock;
70 static 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 // ----------------------------------------------------------------------------
recorderCallback(int event,void * user,void * info)81 static 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 
getJniDeviceCallback(JNIEnv * env,jobject thiz)129 static 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 
setJniDeviceCallback(JNIEnv * env,jobject thiz,const sp<JNIDeviceCallback> & cb)138 static 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 // ----------------------------------------------------------------------------
getAudioRecord(JNIEnv * env,jobject thiz)157 static 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 
setAudioRecord(JNIEnv * env,jobject thiz,const sp<AudioRecord> & ar)165 static 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 // ----------------------------------------------------------------------------
181 static jint
android_media_AudioRecord_setup(JNIEnv * env,jobject thiz,jobject weak_this,jobject jaa,jintArray jSampleRate,jint channelMask,jint channelIndexMask,jint audioFormat,jint buffSizeInBytes,jintArray jSession,jstring opPackageName,jlong nativeRecordInJavaObj)182 android_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:
365 native_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 // ----------------------------------------------------------------------------
378 static jint
android_media_AudioRecord_start(JNIEnv * env,jobject thiz,jint event,jint triggerSession)379 android_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 // ----------------------------------------------------------------------------
393 static void
android_media_AudioRecord_stop(JNIEnv * env,jobject thiz)394 android_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
android_media_AudioRecord_release(JNIEnv * env,jobject thiz)410 static 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 // ----------------------------------------------------------------------------
android_media_AudioRecord_finalize(JNIEnv * env,jobject thiz)445 static void android_media_AudioRecord_finalize(JNIEnv *env,  jobject thiz) {
446     android_media_AudioRecord_release(env, thiz);
447 }
448 
449 // overloaded JNI array helper functions
450 static inline
envGetArrayElements(JNIEnv * env,jbyteArray array,jboolean * isCopy)451 jbyte *envGetArrayElements(JNIEnv *env, jbyteArray array, jboolean *isCopy) {
452     return env->GetByteArrayElements(array, isCopy);
453 }
454 
455 static inline
envReleaseArrayElements(JNIEnv * env,jbyteArray array,jbyte * elems,jint mode)456 void envReleaseArrayElements(JNIEnv *env, jbyteArray array, jbyte *elems, jint mode) {
457     env->ReleaseByteArrayElements(array, elems, mode);
458 }
459 
460 static inline
envGetArrayElements(JNIEnv * env,jshortArray array,jboolean * isCopy)461 jshort *envGetArrayElements(JNIEnv *env, jshortArray array, jboolean *isCopy) {
462     return env->GetShortArrayElements(array, isCopy);
463 }
464 
465 static inline
envReleaseArrayElements(JNIEnv * env,jshortArray array,jshort * elems,jint mode)466 void envReleaseArrayElements(JNIEnv *env, jshortArray array, jshort *elems, jint mode) {
467     env->ReleaseShortArrayElements(array, elems, mode);
468 }
469 
470 static inline
envGetArrayElements(JNIEnv * env,jfloatArray array,jboolean * isCopy)471 jfloat *envGetArrayElements(JNIEnv *env, jfloatArray array, jboolean *isCopy) {
472     return env->GetFloatArrayElements(array, isCopy);
473 }
474 
475 static inline
envReleaseArrayElements(JNIEnv * env,jfloatArray array,jfloat * elems,jint mode)476 void envReleaseArrayElements(JNIEnv *env, jfloatArray array, jfloat *elems, jint mode) {
477     env->ReleaseFloatArrayElements(array, elems, mode);
478 }
479 
480 static inline
interpretReadSizeError(ssize_t readSize)481 jint 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 
492 template <typename T>
android_media_AudioRecord_readInArray(JNIEnv * env,jobject thiz,T javaAudioData,jint offsetInSamples,jint sizeInSamples,jboolean isReadBlocking)493 static 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 // ----------------------------------------------------------------------------
android_media_AudioRecord_readInDirectBuffer(JNIEnv * env,jobject thiz,jobject jBuffer,jint sizeInBytes,jboolean isReadBlocking)535 static 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 // ----------------------------------------------------------------------------
android_media_AudioRecord_get_buffer_size_in_frames(JNIEnv * env,jobject thiz)568 static 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 // ----------------------------------------------------------------------------
android_media_AudioRecord_set_marker_pos(JNIEnv * env,jobject thiz,jint markerPos)579 static 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 // ----------------------------------------------------------------------------
android_media_AudioRecord_get_marker_pos(JNIEnv * env,jobject thiz)593 static 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 // ----------------------------------------------------------------------------
android_media_AudioRecord_set_pos_update_period(JNIEnv * env,jobject thiz,jint period)609 static 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 // ----------------------------------------------------------------------------
android_media_AudioRecord_get_pos_update_period(JNIEnv * env,jobject thiz)624 static 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.
android_media_AudioRecord_get_min_buff_size(JNIEnv * env,jobject thiz,jint sampleRateInHertz,jint channelCount,jint audioFormat)643 static 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 
android_media_AudioRecord_setInputDevice(JNIEnv * env,jobject thiz,jint device_id)665 static 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 
android_media_AudioRecord_getRoutedDeviceId(JNIEnv * env,jobject thiz)675 static 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 
android_media_AudioRecord_enableDeviceCallback(JNIEnv * env,jobject thiz)685 static 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 
android_media_AudioRecord_disableDeviceCallback(JNIEnv * env,jobject thiz)711 static 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 // ----------------------------------------------------------------------------
android_media_AudioRecord_get_timestamp(JNIEnv * env,jobject thiz,jobject timestamp,jint timebase)725 static 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 // ----------------------------------------------------------------------------
755 static 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 // ----------------------------------------------------------------------------
register_android_media_AudioRecord(JNIEnv * env)800 int 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