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