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