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