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