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