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