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 size_t bytesPerSample = audio_bytes_per_sample(format);
216
217 if (buffSizeInBytes == 0) {
218 ALOGE("Error creating AudioRecord: frameCount is 0.");
219 return (jint) AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT;
220 }
221 size_t frameSize = channelCount * bytesPerSample;
222 size_t frameCount = buffSizeInBytes / frameSize;
223
224 // create an uninitialized AudioRecord object
225 Parcel* parcel = parcelForJavaObject(env, jAttributionSource);
226 android::content::AttributionSourceState attributionSource;
227 attributionSource.readFromParcel(parcel);
228
229 lpRecorder = new AudioRecord(attributionSource);
230
231 // read the AudioAttributes values
232 auto paa = JNIAudioAttributeHelper::makeUnique();
233 jint jStatus = JNIAudioAttributeHelper::nativeFromJava(env, jaa, paa.get());
234 if (jStatus != (jint)AUDIO_JAVA_SUCCESS) {
235 return jStatus;
236 }
237 ALOGV("AudioRecord_setup for source=%d tags=%s flags=%08x", paa->source, paa->tags, paa->flags);
238
239 const auto flags = static_cast<audio_input_flags_t>(halFlags);
240 // create the callback information:
241 // this data will be passed with every AudioRecord callback
242 // we use a weak reference so the AudioRecord object can be garbage collected.
243 callbackData = sp<AudioRecordJNIStorage>::make(clazz, weak_this);
244
245 const status_t status =
246 lpRecorder->set(paa->source, sampleRateInHertz,
247 format, // word length, PCM
248 localChanMask, frameCount,
249 callbackData, // callback
250 0, // notificationFrames,
251 true, // threadCanCallJava
252 sessionId, AudioRecord::TRANSFER_DEFAULT, flags, -1,
253 -1, // default uid, pid
254 paa.get(), AUDIO_PORT_HANDLE_NONE, MIC_DIRECTION_UNSPECIFIED,
255 MIC_FIELD_DIMENSION_DEFAULT, sharedAudioHistoryMs);
256
257 if (status != NO_ERROR) {
258 ALOGE("Error creating AudioRecord instance: initialization check failed with status %d.",
259 status);
260 goto native_init_failure;
261 }
262 // Set caller name so it can be logged in destructor.
263 // MediaMetricsConstants.h: AMEDIAMETRICS_PROP_CALLERNAME_VALUE_JAVA
264 lpRecorder->setCallerName("java");
265 } else { // end if nativeRecordInJavaObj == 0)
266 lpRecorder = (AudioRecord*)nativeRecordInJavaObj;
267 // TODO: We need to find out which members of the Java AudioRecord might need to be
268 // initialized from the Native AudioRecord
269 // these are directly returned from getters:
270 // mSampleRate
271 // mRecordSource
272 // mAudioFormat
273 // mChannelMask
274 // mChannelCount
275 // mState (?)
276 // mRecordingState (?)
277 // mPreferredDevice
278
279 // create the callback information:
280 // this data will be passed with every AudioRecord callback
281 // This next line makes little sense
282 // callbackData = sp<AudioRecordJNIStorage>::make(clazz, weak_this);
283 }
284
285 nSession = env->GetIntArrayElements(jSession, nullptr /* isCopy */);
286 if (nSession == NULL) {
287 ALOGE("Error creating AudioRecord: Error retrieving session id pointer");
288 goto native_init_failure;
289 }
290 // read the audio session ID back from AudioRecord in case a new session was created during set()
291 nSession[0] = lpRecorder->getSessionId();
292 env->ReleaseIntArrayElements(jSession, nSession, 0 /* mode */);
293 nSession = NULL;
294
295 {
296 const jint elements[1] = { (jint) lpRecorder->getSampleRate() };
297 env->SetIntArrayRegion(jSampleRate, 0, 1, elements);
298 }
299
300 // save our newly created C++ AudioRecord in the "nativeRecorderInJavaObj" field
301 // of the Java object
302 setFieldSp(env, thiz, lpRecorder, javaAudioRecordFields.nativeRecorderInJavaObj);
303
304 // save our newly created callback information in the "jniData" field
305 // of the Java object (in mNativeJNIDataHandle) so we can free the memory in finalize()
306 setFieldSp(env, thiz, callbackData, javaAudioRecordFields.jniData);
307
308 return (jint) AUDIO_JAVA_SUCCESS;
309
310 // failure:
311 native_init_failure:
312 setFieldSp(env, thiz, sp<AudioRecord>{}, javaAudioRecordFields.nativeRecorderInJavaObj);
313 setFieldSp(env, thiz, sp<AudioRecordJNIStorage>{}, javaAudioRecordFields.jniData);
314
315 // lpRecorder goes out of scope, so reference count drops to zero
316 return (jint) AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED;
317 }
318
319 // ----------------------------------------------------------------------------
320 static jint
android_media_AudioRecord_start(JNIEnv * env,jobject thiz,jint event,jint triggerSession)321 android_media_AudioRecord_start(JNIEnv *env, jobject thiz, jint event, jint triggerSession)
322 {
323 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
324 if (lpRecorder == NULL ) {
325 jniThrowException(env, "java/lang/IllegalStateException", NULL);
326 return (jint) AUDIO_JAVA_ERROR;
327 }
328
329 return nativeToJavaStatus(
330 lpRecorder->start((AudioSystem::sync_event_t)event, (audio_session_t) triggerSession));
331 }
332
333
334 // ----------------------------------------------------------------------------
335 static void
android_media_AudioRecord_stop(JNIEnv * env,jobject thiz)336 android_media_AudioRecord_stop(JNIEnv *env, jobject thiz)
337 {
338 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
339 if (lpRecorder == NULL ) {
340 jniThrowException(env, "java/lang/IllegalStateException", NULL);
341 return;
342 }
343
344 lpRecorder->stop();
345 //ALOGV("Called lpRecorder->stop()");
346 }
347
348
349 // ----------------------------------------------------------------------------
350
351 #define CALLBACK_COND_WAIT_TIMEOUT_MS 1000
android_media_AudioRecord_release(JNIEnv * env,jobject thiz)352 static void android_media_AudioRecord_release(JNIEnv *env, jobject thiz) {
353
354 setFieldSp(env, thiz, sp<AudioRecord>{}, javaAudioRecordFields.nativeRecorderInJavaObj);
355 setFieldSp(env, thiz, sp<AudioRecordJNIStorage>{}, javaAudioRecordFields.jniData);
356 }
357
358
359 // ----------------------------------------------------------------------------
android_media_AudioRecord_finalize(JNIEnv * env,jobject thiz)360 static void android_media_AudioRecord_finalize(JNIEnv *env, jobject thiz) {
361 android_media_AudioRecord_release(env, thiz);
362 }
363
364 // overloaded JNI array helper functions
365 static inline
envGetArrayElements(JNIEnv * env,jbyteArray array,jboolean * isCopy)366 jbyte *envGetArrayElements(JNIEnv *env, jbyteArray array, jboolean *isCopy) {
367 return env->GetByteArrayElements(array, isCopy);
368 }
369
370 static inline
envReleaseArrayElements(JNIEnv * env,jbyteArray array,jbyte * elems,jint mode)371 void envReleaseArrayElements(JNIEnv *env, jbyteArray array, jbyte *elems, jint mode) {
372 env->ReleaseByteArrayElements(array, elems, mode);
373 }
374
375 static inline
envGetArrayElements(JNIEnv * env,jshortArray array,jboolean * isCopy)376 jshort *envGetArrayElements(JNIEnv *env, jshortArray array, jboolean *isCopy) {
377 return env->GetShortArrayElements(array, isCopy);
378 }
379
380 static inline
envReleaseArrayElements(JNIEnv * env,jshortArray array,jshort * elems,jint mode)381 void envReleaseArrayElements(JNIEnv *env, jshortArray array, jshort *elems, jint mode) {
382 env->ReleaseShortArrayElements(array, elems, mode);
383 }
384
385 static inline
envGetArrayElements(JNIEnv * env,jfloatArray array,jboolean * isCopy)386 jfloat *envGetArrayElements(JNIEnv *env, jfloatArray array, jboolean *isCopy) {
387 return env->GetFloatArrayElements(array, isCopy);
388 }
389
390 static inline
envReleaseArrayElements(JNIEnv * env,jfloatArray array,jfloat * elems,jint mode)391 void envReleaseArrayElements(JNIEnv *env, jfloatArray array, jfloat *elems, jint mode) {
392 env->ReleaseFloatArrayElements(array, elems, mode);
393 }
394
395 static inline
interpretReadSizeError(ssize_t readSize)396 jint interpretReadSizeError(ssize_t readSize) {
397 if (readSize == WOULD_BLOCK) {
398 return (jint)0;
399 } else if (readSize == NO_INIT) {
400 return AUDIO_JAVA_DEAD_OBJECT;
401 } else {
402 ALOGE("Error %zd during AudioRecord native read", readSize);
403 return nativeToJavaStatus(readSize);
404 }
405 }
406
407 template <typename T>
android_media_AudioRecord_readInArray(JNIEnv * env,jobject thiz,T javaAudioData,jint offsetInSamples,jint sizeInSamples,jboolean isReadBlocking)408 static jint android_media_AudioRecord_readInArray(JNIEnv *env, jobject thiz,
409 T javaAudioData,
410 jint offsetInSamples, jint sizeInSamples,
411 jboolean isReadBlocking) {
412 // get the audio recorder from which we'll read new audio samples
413 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
414 if (lpRecorder == NULL) {
415 ALOGE("Unable to retrieve AudioRecord object");
416 return (jint)AUDIO_JAVA_INVALID_OPERATION;
417 }
418
419 if (javaAudioData == NULL) {
420 ALOGE("Invalid Java array to store recorded audio");
421 return (jint)AUDIO_JAVA_BAD_VALUE;
422 }
423
424 // NOTE: We may use GetPrimitiveArrayCritical() when the JNI implementation changes in such
425 // a way that it becomes much more efficient. When doing so, we will have to prevent the
426 // AudioSystem callback to be called while in critical section (in case of media server
427 // process crash for instance)
428
429 // get the pointer to where we'll record the audio
430 auto *recordBuff = envGetArrayElements(env, javaAudioData, NULL);
431 if (recordBuff == NULL) {
432 ALOGE("Error retrieving destination for recorded audio data");
433 return (jint)AUDIO_JAVA_BAD_VALUE;
434 }
435
436 // read the new audio data from the native AudioRecord object
437 const size_t sizeInBytes = sizeInSamples * sizeof(*recordBuff);
438 ssize_t readSize = lpRecorder->read(
439 recordBuff + offsetInSamples, sizeInBytes, isReadBlocking == JNI_TRUE /* blocking */);
440
441 envReleaseArrayElements(env, javaAudioData, recordBuff, 0);
442
443 if (readSize < 0) {
444 return interpretReadSizeError(readSize);
445 }
446 return (jint)(readSize / sizeof(*recordBuff));
447 }
448
449 // ----------------------------------------------------------------------------
android_media_AudioRecord_readInDirectBuffer(JNIEnv * env,jobject thiz,jobject jBuffer,jint sizeInBytes,jboolean isReadBlocking)450 static jint android_media_AudioRecord_readInDirectBuffer(JNIEnv *env, jobject thiz,
451 jobject jBuffer, jint sizeInBytes,
452 jboolean isReadBlocking) {
453 // get the audio recorder from which we'll read new audio samples
454 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
455 if (lpRecorder==NULL)
456 return (jint)AUDIO_JAVA_INVALID_OPERATION;
457
458 // direct buffer and direct access supported?
459 long capacity = env->GetDirectBufferCapacity(jBuffer);
460 if (capacity == -1) {
461 // buffer direct access is not supported
462 ALOGE("Buffer direct access is not supported, can't record");
463 return (jint)AUDIO_JAVA_BAD_VALUE;
464 }
465 //ALOGV("capacity = %ld", capacity);
466 jbyte* nativeFromJavaBuf = (jbyte*) env->GetDirectBufferAddress(jBuffer);
467 if (nativeFromJavaBuf==NULL) {
468 ALOGE("Buffer direct access is not supported, can't record");
469 return (jint)AUDIO_JAVA_BAD_VALUE;
470 }
471
472 // read new data from the recorder
473 ssize_t readSize = lpRecorder->read(nativeFromJavaBuf,
474 capacity < sizeInBytes ? capacity : sizeInBytes,
475 isReadBlocking == JNI_TRUE /* blocking */);
476 if (readSize < 0) {
477 return interpretReadSizeError(readSize);
478 }
479 return (jint)readSize;
480 }
481
482 // ----------------------------------------------------------------------------
android_media_AudioRecord_get_buffer_size_in_frames(JNIEnv * env,jobject thiz)483 static jint android_media_AudioRecord_get_buffer_size_in_frames(JNIEnv *env, jobject thiz) {
484 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
485 if (lpRecorder == NULL) {
486 jniThrowException(env, "java/lang/IllegalStateException",
487 "Unable to retrieve AudioRecord pointer for frameCount()");
488 return (jint)AUDIO_JAVA_ERROR;
489 }
490 return lpRecorder->frameCount();
491 }
492
493 // ----------------------------------------------------------------------------
android_media_AudioRecord_set_marker_pos(JNIEnv * env,jobject thiz,jint markerPos)494 static jint android_media_AudioRecord_set_marker_pos(JNIEnv *env, jobject thiz,
495 jint markerPos) {
496
497 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
498 if (lpRecorder == NULL) {
499 jniThrowException(env, "java/lang/IllegalStateException",
500 "Unable to retrieve AudioRecord pointer for setMarkerPosition()");
501 return (jint)AUDIO_JAVA_ERROR;
502 }
503 return nativeToJavaStatus( lpRecorder->setMarkerPosition(markerPos) );
504 }
505
506
507 // ----------------------------------------------------------------------------
android_media_AudioRecord_get_marker_pos(JNIEnv * env,jobject thiz)508 static jint android_media_AudioRecord_get_marker_pos(JNIEnv *env, jobject thiz) {
509
510 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
511 uint32_t markerPos = 0;
512
513 if (lpRecorder == NULL) {
514 jniThrowException(env, "java/lang/IllegalStateException",
515 "Unable to retrieve AudioRecord pointer for getMarkerPosition()");
516 return (jint)AUDIO_JAVA_ERROR;
517 }
518 lpRecorder->getMarkerPosition(&markerPos);
519 return (jint)markerPos;
520 }
521
522
523 // ----------------------------------------------------------------------------
android_media_AudioRecord_set_pos_update_period(JNIEnv * env,jobject thiz,jint period)524 static jint android_media_AudioRecord_set_pos_update_period(JNIEnv *env, jobject thiz,
525 jint period) {
526
527 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
528
529 if (lpRecorder == NULL) {
530 jniThrowException(env, "java/lang/IllegalStateException",
531 "Unable to retrieve AudioRecord pointer for setPositionUpdatePeriod()");
532 return (jint)AUDIO_JAVA_ERROR;
533 }
534 return nativeToJavaStatus( lpRecorder->setPositionUpdatePeriod(period) );
535 }
536
537
538 // ----------------------------------------------------------------------------
android_media_AudioRecord_get_pos_update_period(JNIEnv * env,jobject thiz)539 static jint android_media_AudioRecord_get_pos_update_period(JNIEnv *env, jobject thiz) {
540
541 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
542 uint32_t period = 0;
543
544 if (lpRecorder == NULL) {
545 jniThrowException(env, "java/lang/IllegalStateException",
546 "Unable to retrieve AudioRecord pointer for getPositionUpdatePeriod()");
547 return (jint)AUDIO_JAVA_ERROR;
548 }
549 lpRecorder->getPositionUpdatePeriod(&period);
550 return (jint)period;
551 }
552
553
554 // ----------------------------------------------------------------------------
555 // returns the minimum required size for the successful creation of an AudioRecord instance.
556 // returns 0 if the parameter combination is not supported.
557 // 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)558 static jint android_media_AudioRecord_get_min_buff_size(JNIEnv *env, jobject thiz,
559 jint sampleRateInHertz, jint channelCount, jint audioFormat) {
560
561 ALOGV(">> android_media_AudioRecord_get_min_buff_size(%d, %d, %d)",
562 sampleRateInHertz, channelCount, audioFormat);
563
564 size_t frameCount = 0;
565 audio_format_t format = audioFormatToNative(audioFormat);
566 status_t result = AudioRecord::getMinFrameCount(&frameCount,
567 sampleRateInHertz,
568 format,
569 audio_channel_in_mask_from_count(channelCount));
570
571 if (result == BAD_VALUE) {
572 return 0;
573 }
574 if (result != NO_ERROR) {
575 return -1;
576 }
577 return frameCount * channelCount * audio_bytes_per_sample(format);
578 }
579
android_media_AudioRecord_setInputDevice(JNIEnv * env,jobject thiz,jint device_id)580 static jboolean android_media_AudioRecord_setInputDevice(
581 JNIEnv *env, jobject thiz, jint device_id) {
582
583 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
584 if (lpRecorder == 0) {
585 return false;
586 }
587 return lpRecorder->setInputDevice(device_id) == NO_ERROR;
588 }
589
android_media_AudioRecord_getRoutedDeviceId(JNIEnv * env,jobject thiz)590 static jint android_media_AudioRecord_getRoutedDeviceId(
591 JNIEnv *env, jobject thiz) {
592
593 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
594 if (lpRecorder == 0) {
595 return 0;
596 }
597 return (jint)lpRecorder->getRoutedDeviceId();
598 }
599
600 // Enable and Disable Callback methods are synchronized on the Java side
android_media_AudioRecord_enableDeviceCallback(JNIEnv * env,jobject thiz)601 static void android_media_AudioRecord_enableDeviceCallback(
602 JNIEnv *env, jobject thiz) {
603
604 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
605 if (lpRecorder == nullptr) {
606 return;
607 }
608 const auto pJniStorage =
609 getFieldSp<AudioRecordJNIStorage>(env, thiz, javaAudioRecordFields.jniData);
610 if (pJniStorage == nullptr || pJniStorage->getDeviceCallback() != nullptr) {
611 return;
612 }
613
614 pJniStorage->setDeviceCallback(
615 sp<JNIDeviceCallback>::make(env, thiz, pJniStorage->getAudioTrackWeakRef(),
616 javaAudioRecordFields.postNativeEventInJava));
617 lpRecorder->addAudioDeviceCallback(pJniStorage->getDeviceCallback());
618 }
619
android_media_AudioRecord_disableDeviceCallback(JNIEnv * env,jobject thiz)620 static void android_media_AudioRecord_disableDeviceCallback(
621 JNIEnv *env, jobject thiz) {
622 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
623 if (lpRecorder == nullptr) {
624 return;
625 }
626 const auto pJniStorage =
627 getFieldSp<AudioRecordJNIStorage>(env, thiz, javaAudioRecordFields.jniData);
628
629 if (pJniStorage == nullptr || pJniStorage->getDeviceCallback() == nullptr) {
630 return;
631 }
632 lpRecorder->removeAudioDeviceCallback(pJniStorage->getDeviceCallback());
633 pJniStorage->setDeviceCallback(nullptr);
634 }
635
636 // ----------------------------------------------------------------------------
android_media_AudioRecord_get_timestamp(JNIEnv * env,jobject thiz,jobject timestamp,jint timebase)637 static jint android_media_AudioRecord_get_timestamp(JNIEnv *env, jobject thiz,
638 jobject timestamp, jint timebase) {
639 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
640
641 if (lpRecorder == NULL) {
642 jniThrowException(env, "java/lang/IllegalStateException",
643 "Unable to retrieve AudioRecord pointer for getTimestamp()");
644 return (jint)AUDIO_JAVA_ERROR;
645 }
646
647 ExtendedTimestamp ts;
648 jint status = nativeToJavaStatus(lpRecorder->getTimestamp(&ts));
649
650 if (status == AUDIO_JAVA_SUCCESS) {
651 // set the data
652 int64_t position, time;
653
654 status = nativeToJavaStatus(ts.getBestTimestamp(&position, &time, timebase));
655 if (status == AUDIO_JAVA_SUCCESS) {
656 env->SetLongField(
657 timestamp, javaAudioTimestampFields.fieldFramePosition, position);
658 env->SetLongField(
659 timestamp, javaAudioTimestampFields.fieldNanoTime, time);
660 }
661 }
662 return status;
663 }
664
665 // ----------------------------------------------------------------------------
666 static jobject
android_media_AudioRecord_native_getMetrics(JNIEnv * env,jobject thiz)667 android_media_AudioRecord_native_getMetrics(JNIEnv *env, jobject thiz)
668 {
669 ALOGV("android_media_AudioRecord_native_getMetrics");
670
671 sp<AudioRecord> lpRecord = getAudioRecord(env, thiz);
672
673 if (lpRecord == NULL) {
674 ALOGE("Unable to retrieve AudioRecord pointer for getMetrics()");
675 jniThrowException(env, "java/lang/IllegalStateException", NULL);
676 return (jobject) NULL;
677 }
678
679 // get what we have for the metrics from the record session
680 mediametrics::Item *item = NULL;
681
682 status_t err = lpRecord->getMetrics(item);
683 if (err != OK) {
684 ALOGE("getMetrics failed");
685 jniThrowException(env, "java/lang/IllegalStateException", NULL);
686 return (jobject) NULL;
687 }
688
689 jobject mybundle = MediaMetricsJNI::writeMetricsToBundle(env, item, NULL /* mybundle */);
690
691 // housekeeping
692 delete item;
693 item = NULL;
694
695 return mybundle;
696 }
697
698 // ----------------------------------------------------------------------------
android_media_AudioRecord_get_active_microphones(JNIEnv * env,jobject thiz,jobject jActiveMicrophones)699 static jint android_media_AudioRecord_get_active_microphones(JNIEnv *env,
700 jobject thiz, jobject jActiveMicrophones) {
701 if (jActiveMicrophones == NULL) {
702 ALOGE("jActiveMicrophones is null");
703 return (jint)AUDIO_JAVA_BAD_VALUE;
704 }
705 if (!env->IsInstanceOf(jActiveMicrophones, gArrayListClass)) {
706 ALOGE("getActiveMicrophones not an arraylist");
707 return (jint)AUDIO_JAVA_BAD_VALUE;
708 }
709
710 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
711 if (lpRecorder == NULL) {
712 jniThrowException(env, "java/lang/IllegalStateException",
713 "Unable to retrieve AudioRecord pointer for getActiveMicrophones()");
714 return (jint)AUDIO_JAVA_ERROR;
715 }
716
717 jint jStatus = AUDIO_JAVA_SUCCESS;
718 std::vector<media::MicrophoneInfoFw> activeMicrophones;
719 status_t status = lpRecorder->getActiveMicrophones(&activeMicrophones);
720 if (status != NO_ERROR) {
721 ALOGE_IF(status != NO_ERROR, "AudioRecord::getActiveMicrophones error %d", status);
722 jStatus = nativeToJavaStatus(status);
723 return jStatus;
724 }
725
726 for (size_t i = 0; i < activeMicrophones.size(); i++) {
727 jobject jMicrophoneInfo;
728 jStatus = convertMicrophoneInfoFromNative(env, &jMicrophoneInfo, &activeMicrophones[i]);
729 if (jStatus != AUDIO_JAVA_SUCCESS) {
730 return jStatus;
731 }
732 env->CallBooleanMethod(jActiveMicrophones, gArrayListMethods.add, jMicrophoneInfo);
733 env->DeleteLocalRef(jMicrophoneInfo);
734 }
735 return jStatus;
736 }
737
android_media_AudioRecord_set_preferred_microphone_direction(JNIEnv * env,jobject thiz,jint direction)738 static int android_media_AudioRecord_set_preferred_microphone_direction(
739 JNIEnv *env, jobject thiz, jint direction) {
740 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
741 if (lpRecorder == NULL) {
742 jniThrowException(env, "java/lang/IllegalStateException",
743 "Unable to retrieve AudioRecord pointer for setPreferredMicrophoneDirection()");
744 return (jint)AUDIO_JAVA_ERROR;
745 }
746
747 jint jStatus = AUDIO_JAVA_SUCCESS;
748 status_t status = lpRecorder->setPreferredMicrophoneDirection(
749 static_cast<audio_microphone_direction_t>(direction));
750 if (status != NO_ERROR) {
751 jStatus = nativeToJavaStatus(status);
752 }
753
754 return jStatus;
755 }
756
android_media_AudioRecord_set_preferred_microphone_field_dimension(JNIEnv * env,jobject thiz,jfloat zoom)757 static int android_media_AudioRecord_set_preferred_microphone_field_dimension(
758 JNIEnv *env, jobject thiz, jfloat zoom) {
759 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
760 if (lpRecorder == NULL) {
761 jniThrowException(env, "java/lang/IllegalStateException",
762 "Unable to retrieve AudioRecord pointer for setPreferredMicrophoneFieldDimension()");
763 return (jint)AUDIO_JAVA_ERROR;
764 }
765
766 jint jStatus = AUDIO_JAVA_SUCCESS;
767 status_t status = lpRecorder->setPreferredMicrophoneFieldDimension(zoom);
768 if (status != NO_ERROR) {
769 jStatus = nativeToJavaStatus(status);
770 }
771
772 return jStatus;
773 }
774
android_media_AudioRecord_setLogSessionId(JNIEnv * env,jobject thiz,jstring jlogSessionId)775 static void android_media_AudioRecord_setLogSessionId(JNIEnv *env, jobject thiz,
776 jstring jlogSessionId) {
777 sp<AudioRecord> record = getAudioRecord(env, thiz);
778 if (record == nullptr) {
779 jniThrowException(env, "java/lang/IllegalStateException",
780 "Unable to retrieve AudioRecord pointer for setLogSessionId()");
781 }
782 if (jlogSessionId == nullptr) {
783 ALOGV("%s: logSessionId nullptr", __func__);
784 record->setLogSessionId(nullptr);
785 return;
786 }
787 ScopedUtfChars logSessionId(env, jlogSessionId);
788 ALOGV("%s: logSessionId '%s'", __func__, logSessionId.c_str());
789 record->setLogSessionId(logSessionId.c_str());
790 }
791
android_media_AudioRecord_shareAudioHistory(JNIEnv * env,jobject thiz,jstring jSharedPackageName,jlong jSharedStartMs)792 static jint android_media_AudioRecord_shareAudioHistory(JNIEnv *env, jobject thiz,
793 jstring jSharedPackageName,
794 jlong jSharedStartMs) {
795 sp<AudioRecord> record = getAudioRecord(env, thiz);
796 if (record == nullptr) {
797 jniThrowException(env, "java/lang/IllegalStateException",
798 "Unable to retrieve AudioRecord pointer for setLogSessionId()");
799 }
800 if (jSharedPackageName == nullptr) {
801 jniThrowException(env, "java/lang/IllegalArgumentException", "package name cannot be null");
802 }
803 ScopedUtfChars nSharedPackageName(env, jSharedPackageName);
804 ALOGV("%s: nSharedPackageName '%s'", __func__, nSharedPackageName.c_str());
805 return nativeToJavaStatus(record->shareAudioHistory(nSharedPackageName.c_str(),
806 static_cast<int64_t>(jSharedStartMs)));
807 }
808
809 // ----------------------------------------------------------------------------
android_media_AudioRecord_get_port_id(JNIEnv * env,jobject thiz)810 static jint android_media_AudioRecord_get_port_id(JNIEnv *env, jobject thiz) {
811 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
812 if (lpRecorder == NULL) {
813 jniThrowException(env, "java/lang/IllegalStateException",
814 "Unable to retrieve AudioRecord pointer for getId()");
815 return (jint)AUDIO_PORT_HANDLE_NONE;
816 }
817 return (jint)lpRecorder->getPortId();
818 }
819
820
821 // ----------------------------------------------------------------------------
822 // ----------------------------------------------------------------------------
823 static const JNINativeMethod gMethods[] = {
824 // name, signature, funcPtr
825 {"native_start", "(II)I", (void *)android_media_AudioRecord_start},
826 {"native_stop", "()V", (void *)android_media_AudioRecord_stop},
827 {"native_setup",
828 "(Ljava/lang/Object;Ljava/lang/Object;[IIIII[ILandroid/os/Parcel;JII)I",
829 (void *)android_media_AudioRecord_setup},
830 {"native_finalize", "()V", (void *)android_media_AudioRecord_finalize},
831 {"native_release", "()V", (void *)android_media_AudioRecord_release},
832 {"native_read_in_byte_array", "([BIIZ)I",
833 (void *)android_media_AudioRecord_readInArray<jbyteArray>},
834 {"native_read_in_short_array", "([SIIZ)I",
835 (void *)android_media_AudioRecord_readInArray<jshortArray>},
836 {"native_read_in_float_array", "([FIIZ)I",
837 (void *)android_media_AudioRecord_readInArray<jfloatArray>},
838 {"native_read_in_direct_buffer", "(Ljava/lang/Object;IZ)I",
839 (void *)android_media_AudioRecord_readInDirectBuffer},
840 {"native_get_buffer_size_in_frames", "()I",
841 (void *)android_media_AudioRecord_get_buffer_size_in_frames},
842 {"native_set_marker_pos", "(I)I", (void *)android_media_AudioRecord_set_marker_pos},
843 {"native_get_marker_pos", "()I", (void *)android_media_AudioRecord_get_marker_pos},
844 {"native_set_pos_update_period", "(I)I",
845 (void *)android_media_AudioRecord_set_pos_update_period},
846 {"native_get_pos_update_period", "()I",
847 (void *)android_media_AudioRecord_get_pos_update_period},
848 {"native_get_min_buff_size", "(III)I", (void *)android_media_AudioRecord_get_min_buff_size},
849 {"native_getMetrics", "()Landroid/os/PersistableBundle;",
850 (void *)android_media_AudioRecord_native_getMetrics},
851 {"native_setInputDevice", "(I)Z", (void *)android_media_AudioRecord_setInputDevice},
852 {"native_getRoutedDeviceId", "()I", (void *)android_media_AudioRecord_getRoutedDeviceId},
853 {"native_enableDeviceCallback", "()V",
854 (void *)android_media_AudioRecord_enableDeviceCallback},
855 {"native_disableDeviceCallback", "()V",
856 (void *)android_media_AudioRecord_disableDeviceCallback},
857 {"native_get_timestamp", "(Landroid/media/AudioTimestamp;I)I",
858 (void *)android_media_AudioRecord_get_timestamp},
859 {"native_get_active_microphones", "(Ljava/util/ArrayList;)I",
860 (void *)android_media_AudioRecord_get_active_microphones},
861 {"native_getPortId", "()I", (void *)android_media_AudioRecord_get_port_id},
862 {"native_set_preferred_microphone_direction", "(I)I",
863 (void *)android_media_AudioRecord_set_preferred_microphone_direction},
864 {"native_set_preferred_microphone_field_dimension", "(F)I",
865 (void *)android_media_AudioRecord_set_preferred_microphone_field_dimension},
866 {"native_setLogSessionId", "(Ljava/lang/String;)V",
867 (void *)android_media_AudioRecord_setLogSessionId},
868 {"native_shareAudioHistory", "(Ljava/lang/String;J)I",
869 (void *)android_media_AudioRecord_shareAudioHistory},
870 };
871
872 // field names found in android/media/AudioRecord.java
873 #define JAVA_POSTEVENT_CALLBACK_NAME "postEventFromNative"
874 #define JAVA_NATIVEAUDIORECORDERHANDLE_FIELD_NAME "mNativeAudioRecordHandle"
875 #define JAVA_NATIVEJNIDATAHANDLE_FIELD_NAME "mNativeJNIDataHandle"
876
877 // ----------------------------------------------------------------------------
register_android_media_AudioRecord(JNIEnv * env)878 int register_android_media_AudioRecord(JNIEnv *env)
879 {
880 javaAudioRecordFields.postNativeEventInJava = NULL;
881 javaAudioRecordFields.nativeRecorderInJavaObj = NULL;
882 javaAudioRecordFields.jniData = NULL;
883
884
885 // Get the AudioRecord class
886 jclass audioRecordClass = FindClassOrDie(env, kClassPathName);
887 // Get the postEvent method
888 javaAudioRecordFields.postNativeEventInJava = GetStaticMethodIDOrDie(env,
889 audioRecordClass, JAVA_POSTEVENT_CALLBACK_NAME,
890 "(Ljava/lang/Object;IIILjava/lang/Object;)V");
891
892 // Get the variables
893 // mNativeAudioRecordHandle
894 javaAudioRecordFields.nativeRecorderInJavaObj = GetFieldIDOrDie(env,
895 audioRecordClass, JAVA_NATIVEAUDIORECORDERHANDLE_FIELD_NAME, "J");
896 // mNativeJNIDataHandle
897 javaAudioRecordFields.jniData = GetFieldIDOrDie(env,
898 audioRecordClass, JAVA_NATIVEJNIDATAHANDLE_FIELD_NAME, "J");
899
900 // Get the RecordTimestamp class and fields
901 jclass audioTimestampClass = FindClassOrDie(env, "android/media/AudioTimestamp");
902 javaAudioTimestampFields.fieldFramePosition =
903 GetFieldIDOrDie(env, audioTimestampClass, "framePosition", "J");
904 javaAudioTimestampFields.fieldNanoTime =
905 GetFieldIDOrDie(env, audioTimestampClass, "nanoTime", "J");
906
907 jclass arrayListClass = FindClassOrDie(env, "java/util/ArrayList");
908 gArrayListClass = MakeGlobalRefOrDie(env, arrayListClass);
909 gArrayListMethods.add = GetMethodIDOrDie(env, arrayListClass, "add", "(Ljava/lang/Object;)Z");
910
911 return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
912 }
913
914 // ----------------------------------------------------------------------------
915