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