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
41 using namespace android;
42
43 // ----------------------------------------------------------------------------
44 static const char* const kClassPathName = "android/media/AudioRecord";
45 static const char* const kAudioAttributesClassPathName = "android/media/AudioAttributes";
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 struct audio_attributes_fields_t {
60 jfieldID fieldRecSource; // AudioAttributes.mSource
61 jfieldID fieldFlags; // AudioAttributes.mFlags
62 jfieldID fieldFormattedTags;// AudioAttributes.mFormattedTags
63 };
64 static audio_attributes_fields_t javaAudioAttrFields;
65 static audio_record_fields_t javaAudioRecordFields;
66 static struct {
67 jfieldID fieldFramePosition; // AudioTimestamp.framePosition
68 jfieldID fieldNanoTime; // AudioTimestamp.nanoTime
69 } javaAudioTimestampFields;
70
71 struct audiorecord_callback_cookie {
72 jclass audioRecord_class;
73 jobject audioRecord_ref;
74 bool busy;
75 Condition cond;
76 };
77
78 static Mutex sLock;
79 static 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 // ----------------------------------------------------------------------------
recorderCallback(int event,void * user,void * info)90 static 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
getJniDeviceCallback(JNIEnv * env,jobject thiz)138 static 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
setJniDeviceCallback(JNIEnv * env,jobject thiz,const sp<JNIDeviceCallback> & cb)147 static 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 // ----------------------------------------------------------------------------
getAudioRecord(JNIEnv * env,jobject thiz)166 static 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
setAudioRecord(JNIEnv * env,jobject thiz,const sp<AudioRecord> & ar)174 static 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 // ----------------------------------------------------------------------------
190 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)191 android_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:
380 native_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 // ----------------------------------------------------------------------------
396 static jint
android_media_AudioRecord_start(JNIEnv * env,jobject thiz,jint event,jint triggerSession)397 android_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 // ----------------------------------------------------------------------------
411 static void
android_media_AudioRecord_stop(JNIEnv * env,jobject thiz)412 android_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
android_media_AudioRecord_release(JNIEnv * env,jobject thiz)428 static 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 // ----------------------------------------------------------------------------
android_media_AudioRecord_finalize(JNIEnv * env,jobject thiz)463 static void android_media_AudioRecord_finalize(JNIEnv *env, jobject thiz) {
464 android_media_AudioRecord_release(env, thiz);
465 }
466
467 // overloaded JNI array helper functions
468 static inline
envGetArrayElements(JNIEnv * env,jbyteArray array,jboolean * isCopy)469 jbyte *envGetArrayElements(JNIEnv *env, jbyteArray array, jboolean *isCopy) {
470 return env->GetByteArrayElements(array, isCopy);
471 }
472
473 static inline
envReleaseArrayElements(JNIEnv * env,jbyteArray array,jbyte * elems,jint mode)474 void envReleaseArrayElements(JNIEnv *env, jbyteArray array, jbyte *elems, jint mode) {
475 env->ReleaseByteArrayElements(array, elems, mode);
476 }
477
478 static inline
envGetArrayElements(JNIEnv * env,jshortArray array,jboolean * isCopy)479 jshort *envGetArrayElements(JNIEnv *env, jshortArray array, jboolean *isCopy) {
480 return env->GetShortArrayElements(array, isCopy);
481 }
482
483 static inline
envReleaseArrayElements(JNIEnv * env,jshortArray array,jshort * elems,jint mode)484 void envReleaseArrayElements(JNIEnv *env, jshortArray array, jshort *elems, jint mode) {
485 env->ReleaseShortArrayElements(array, elems, mode);
486 }
487
488 static inline
envGetArrayElements(JNIEnv * env,jfloatArray array,jboolean * isCopy)489 jfloat *envGetArrayElements(JNIEnv *env, jfloatArray array, jboolean *isCopy) {
490 return env->GetFloatArrayElements(array, isCopy);
491 }
492
493 static inline
envReleaseArrayElements(JNIEnv * env,jfloatArray array,jfloat * elems,jint mode)494 void envReleaseArrayElements(JNIEnv *env, jfloatArray array, jfloat *elems, jint mode) {
495 env->ReleaseFloatArrayElements(array, elems, mode);
496 }
497
498 static inline
interpretReadSizeError(ssize_t readSize)499 jint 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
510 template <typename T>
android_media_AudioRecord_readInArray(JNIEnv * env,jobject thiz,T javaAudioData,jint offsetInSamples,jint sizeInSamples,jboolean isReadBlocking)511 static 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 // ----------------------------------------------------------------------------
android_media_AudioRecord_readInDirectBuffer(JNIEnv * env,jobject thiz,jobject jBuffer,jint sizeInBytes,jboolean isReadBlocking)553 static 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 // ----------------------------------------------------------------------------
android_media_AudioRecord_get_buffer_size_in_frames(JNIEnv * env,jobject thiz)586 static 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 // ----------------------------------------------------------------------------
android_media_AudioRecord_set_marker_pos(JNIEnv * env,jobject thiz,jint markerPos)597 static 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 // ----------------------------------------------------------------------------
android_media_AudioRecord_get_marker_pos(JNIEnv * env,jobject thiz)611 static 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 // ----------------------------------------------------------------------------
android_media_AudioRecord_set_pos_update_period(JNIEnv * env,jobject thiz,jint period)627 static 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 // ----------------------------------------------------------------------------
android_media_AudioRecord_get_pos_update_period(JNIEnv * env,jobject thiz)642 static 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.
android_media_AudioRecord_get_min_buff_size(JNIEnv * env,jobject thiz,jint sampleRateInHertz,jint channelCount,jint audioFormat)661 static 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
android_media_AudioRecord_setInputDevice(JNIEnv * env,jobject thiz,jint device_id)683 static 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
android_media_AudioRecord_getRoutedDeviceId(JNIEnv * env,jobject thiz)693 static 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
android_media_AudioRecord_enableDeviceCallback(JNIEnv * env,jobject thiz)703 static 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
android_media_AudioRecord_disableDeviceCallback(JNIEnv * env,jobject thiz)729 static 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 // ----------------------------------------------------------------------------
android_media_AudioRecord_get_timestamp(JNIEnv * env,jobject thiz,jobject timestamp,jint timebase)743 static 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 // ----------------------------------------------------------------------------
772 static jobject
android_media_AudioRecord_native_getMetrics(JNIEnv * env,jobject thiz)773 android_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 // ----------------------------------------------------------------------------
android_media_AudioRecord_get_active_microphones(JNIEnv * env,jobject thiz,jobject jActiveMicrophones)805 static 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 // ----------------------------------------------------------------------------
846 static 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 // ----------------------------------------------------------------------------
register_android_media_AudioRecord(JNIEnv * env)895 int 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