blob: ebd16c7084ac579b61adcea9cd66ed1861139141 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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
Kévin PETIT95ece352014-02-13 11:02:27 +000021#include <inttypes.h>
Glenn Kastenc81d31c2012-03-13 14:46:23 -070022#include <jni.h>
Steven Moreland2279b252017-07-19 09:50:45 -070023#include <nativehelper/JNIHelp.h>
Andreas Gampeed6b9df2014-11-20 22:02:20 -080024#include "core_jni_helpers.h"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080025
Glenn Kastenc81d31c2012-03-13 14:46:23 -070026#include <utils/Log.h>
27#include <media/AudioRecord.h>
Dima Zavin24fc2fb2011-04-19 22:30:36 -070028
Steven Moreland2279b252017-07-19 09:50:45 -070029#include <nativehelper/ScopedUtfChars.h>
Svet Ganovfa5ecdc2015-04-28 12:03:28 -070030
Glenn Kastenfe834d32014-01-08 14:49:08 -080031#include "android_media_AudioFormat.h"
Eric Laurentbc11a692014-05-16 12:19:25 -070032#include "android_media_AudioErrors.h"
Eric Laurent4bcdba82015-05-01 11:37:49 -070033#include "android_media_DeviceCallback.h"
Ray Essick510225b2018-01-24 14:27:16 -080034#include "android_media_MediaMetricsJNI.h"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080035
36// ----------------------------------------------------------------------------
37
38using namespace android;
39
40// ----------------------------------------------------------------------------
41static const char* const kClassPathName = "android/media/AudioRecord";
Jean-Michel Trivi701d6ff2014-07-16 07:51:22 -070042static const char* const kAudioAttributesClassPathName = "android/media/AudioAttributes";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080043
Jean-Michel Trivi701d6ff2014-07-16 07:51:22 -070044struct audio_record_fields_t {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080045 // these fields provide access from C++ to the...
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080046 jmethodID postNativeEventInJava; //... event post callback method
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080047 jfieldID nativeRecorderInJavaObj; // provides access to the C++ AudioRecord object
48 jfieldID nativeCallbackCookie; // provides access to the AudioRecord callback data
Eric Laurent4bcdba82015-05-01 11:37:49 -070049 jfieldID nativeDeviceCallback; // provides access to the JNIDeviceCallback instance
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080050};
Jean-Michel Trivi701d6ff2014-07-16 07:51:22 -070051struct audio_attributes_fields_t {
52 jfieldID fieldRecSource; // AudioAttributes.mSource
Eric Laurentbdad1af2014-09-19 17:43:29 -070053 jfieldID fieldFlags; // AudioAttributes.mFlags
Jean-Michel Trivi701d6ff2014-07-16 07:51:22 -070054 jfieldID fieldFormattedTags;// AudioAttributes.mFormattedTags
55};
56static audio_attributes_fields_t javaAudioAttrFields;
57static audio_record_fields_t javaAudioRecordFields;
Andy Hung0ad99c02016-01-15 17:53:47 -080058static struct {
59 jfieldID fieldFramePosition; // AudioTimestamp.framePosition
60 jfieldID fieldNanoTime; // AudioTimestamp.nanoTime
61} javaAudioTimestampFields;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080062
63struct audiorecord_callback_cookie {
64 jclass audioRecord_class;
65 jobject audioRecord_ref;
Eric Laurent532bc1c2012-04-20 12:45:03 -070066 bool busy;
67 Condition cond;
68};
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080069
Eric Laurent532bc1c2012-04-20 12:45:03 -070070static Mutex sLock;
71static SortedVector <audiorecord_callback_cookie *> sAudioRecordCallBackCookies;
Dave Sparkse6335c92010-03-13 17:08:22 -080072
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080073// ----------------------------------------------------------------------------
74
Chih-Hung Hsieh0ca16ef2016-05-19 15:14:54 -070075#define AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT (-16)
76#define AUDIORECORD_ERROR_SETUP_INVALIDCHANNELMASK (-17)
77#define AUDIORECORD_ERROR_SETUP_INVALIDFORMAT (-18)
78#define AUDIORECORD_ERROR_SETUP_INVALIDSOURCE (-19)
79#define AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED (-20)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080080
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080081// ----------------------------------------------------------------------------
82static void recorderCallback(int event, void* user, void *info) {
Eric Laurent532bc1c2012-04-20 12:45:03 -070083
84 audiorecord_callback_cookie *callbackInfo = (audiorecord_callback_cookie *)user;
85 {
86 Mutex::Autolock l(sLock);
87 if (sAudioRecordCallBackCookies.indexOf(callbackInfo) < 0) {
88 return;
89 }
90 callbackInfo->busy = true;
91 }
Glenn Kasten18db49a2012-03-12 16:29:55 -070092
Glenn Kasten5b1576c2013-07-18 16:58:19 -070093 switch (event) {
94 case AudioRecord::EVENT_MARKER: {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080095 JNIEnv *env = AndroidRuntime::getJNIEnv();
Glenn Kastena667ff32013-07-22 07:36:34 -070096 if (user != NULL && env != NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080097 env->CallStaticVoidMethod(
Glenn Kasten18db49a2012-03-12 16:29:55 -070098 callbackInfo->audioRecord_class,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080099 javaAudioRecordFields.postNativeEventInJava,
100 callbackInfo->audioRecord_ref, event, 0,0, NULL);
101 if (env->ExceptionCheck()) {
102 env->ExceptionDescribe();
103 env->ExceptionClear();
104 }
105 }
Glenn Kasten5b1576c2013-07-18 16:58:19 -0700106 } break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800107
Glenn Kasten5b1576c2013-07-18 16:58:19 -0700108 case AudioRecord::EVENT_NEW_POS: {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800109 JNIEnv *env = AndroidRuntime::getJNIEnv();
Glenn Kastena667ff32013-07-22 07:36:34 -0700110 if (user != NULL && env != NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800111 env->CallStaticVoidMethod(
Glenn Kasten18db49a2012-03-12 16:29:55 -0700112 callbackInfo->audioRecord_class,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800113 javaAudioRecordFields.postNativeEventInJava,
114 callbackInfo->audioRecord_ref, event, 0,0, NULL);
115 if (env->ExceptionCheck()) {
116 env->ExceptionDescribe();
117 env->ExceptionClear();
118 }
119 }
Glenn Kasten5b1576c2013-07-18 16:58:19 -0700120 } break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800121 }
Glenn Kasten5b1576c2013-07-18 16:58:19 -0700122
Eric Laurent532bc1c2012-04-20 12:45:03 -0700123 {
124 Mutex::Autolock l(sLock);
125 callbackInfo->busy = false;
126 callbackInfo->cond.broadcast();
127 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800128}
129
Eric Laurent4bcdba82015-05-01 11:37:49 -0700130static sp<JNIDeviceCallback> getJniDeviceCallback(JNIEnv* env, jobject thiz)
131{
132 Mutex::Autolock l(sLock);
133 JNIDeviceCallback* const cb =
134 (JNIDeviceCallback*)env->GetLongField(thiz,
135 javaAudioRecordFields.nativeDeviceCallback);
136 return sp<JNIDeviceCallback>(cb);
137}
138
139static sp<JNIDeviceCallback> setJniDeviceCallback(JNIEnv* env,
140 jobject thiz,
141 const sp<JNIDeviceCallback>& cb)
142{
143 Mutex::Autolock l(sLock);
144 sp<JNIDeviceCallback> old =
145 (JNIDeviceCallback*)env->GetLongField(thiz,
146 javaAudioRecordFields.nativeDeviceCallback);
147 if (cb.get()) {
148 cb->incStrong((void*)setJniDeviceCallback);
149 }
150 if (old != 0) {
151 old->decStrong((void*)setJniDeviceCallback);
152 }
153 env->SetLongField(thiz, javaAudioRecordFields.nativeDeviceCallback, (jlong)cb.get());
154 return old;
155}
156
Eric Laurent532bc1c2012-04-20 12:45:03 -0700157// ----------------------------------------------------------------------------
158static sp<AudioRecord> getAudioRecord(JNIEnv* env, jobject thiz)
159{
160 Mutex::Autolock l(sLock);
161 AudioRecord* const ar =
Ashok Bhat075e9a12014-01-06 13:45:09 +0000162 (AudioRecord*)env->GetLongField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
Eric Laurent532bc1c2012-04-20 12:45:03 -0700163 return sp<AudioRecord>(ar);
164}
165
166static sp<AudioRecord> setAudioRecord(JNIEnv* env, jobject thiz, const sp<AudioRecord>& ar)
167{
168 Mutex::Autolock l(sLock);
169 sp<AudioRecord> old =
Ashok Bhat075e9a12014-01-06 13:45:09 +0000170 (AudioRecord*)env->GetLongField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
Eric Laurent532bc1c2012-04-20 12:45:03 -0700171 if (ar.get()) {
Mathias Agopianb1d90c82013-03-06 17:45:42 -0800172 ar->incStrong((void*)setAudioRecord);
Eric Laurent532bc1c2012-04-20 12:45:03 -0700173 }
174 if (old != 0) {
Mathias Agopianb1d90c82013-03-06 17:45:42 -0800175 old->decStrong((void*)setAudioRecord);
Eric Laurent532bc1c2012-04-20 12:45:03 -0700176 }
Ashok Bhat075e9a12014-01-06 13:45:09 +0000177 env->SetLongField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj, (jlong)ar.get());
Eric Laurent532bc1c2012-04-20 12:45:03 -0700178 return old;
179}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800180
181// ----------------------------------------------------------------------------
Ashok Bhat075e9a12014-01-06 13:45:09 +0000182static jint
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800183android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject weak_this,
Glenn Kasten1cbf9b32016-02-02 12:04:09 -0800184 jobject jaa, jintArray jSampleRate, jint channelMask, jint channelIndexMask,
Paul McLean9b09e532016-01-26 14:43:35 -0700185 jint audioFormat, jint buffSizeInBytes, jintArray jSession, jstring opPackageName,
186 jlong nativeRecordInJavaObj)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800187{
Steve Block71f2cf12011-10-20 11:56:00 +0100188 //ALOGV(">> Entering android_media_AudioRecord_setup");
Paul McLean9b09e532016-01-26 14:43:35 -0700189 //ALOGV("sampleRate=%d, audioFormat=%d, channel mask=%x, buffSizeInBytes=%d "
190 // "nativeRecordInJavaObj=0x%llX",
191 // sampleRateInHertz, audioFormat, channelMask, buffSizeInBytes, nativeRecordInJavaObj);
192 audio_channel_mask_t localChanMask = inChannelMaskToNative(channelMask);
Eric Laurent532bc1c2012-04-20 12:45:03 -0700193
Eric Laurent44ff4cd2011-06-18 10:34:05 -0700194 if (jSession == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000195 ALOGE("Error creating AudioRecord: invalid session ID pointer");
Eric Laurentbc11a692014-05-16 12:19:25 -0700196 return (jint) AUDIO_JAVA_ERROR;
Eric Laurent44ff4cd2011-06-18 10:34:05 -0700197 }
198
199 jint* nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
200 if (nSession == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000201 ALOGE("Error creating AudioRecord: Error retrieving session id pointer");
Eric Laurentbc11a692014-05-16 12:19:25 -0700202 return (jint) AUDIO_JAVA_ERROR;
Eric Laurent44ff4cd2011-06-18 10:34:05 -0700203 }
Glenn Kasten33b84042016-03-08 12:02:55 -0800204 audio_session_t sessionId = (audio_session_t) nSession[0];
Eric Laurent44ff4cd2011-06-18 10:34:05 -0700205 env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
206 nSession = NULL;
207
Jean-Michel Trivi701d6ff2014-07-16 07:51:22 -0700208 audio_attributes_t *paa = NULL;
Paul McLean9b09e532016-01-26 14:43:35 -0700209 sp<AudioRecord> lpRecorder = 0;
210 audiorecord_callback_cookie *lpCallbackData = NULL;
Jean-Michel Trivi701d6ff2014-07-16 07:51:22 -0700211
Paul McLean9b09e532016-01-26 14:43:35 -0700212 jclass clazz = env->GetObjectClass(thiz);
213 if (clazz == NULL) {
214 ALOGE("Can't find %s when setting up callback.", kClassPathName);
215 return (jint) AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED;
Eric Laurentbdad1af2014-09-19 17:43:29 -0700216 }
Glenn Kasten18db49a2012-03-12 16:29:55 -0700217
Paul McLean9b09e532016-01-26 14:43:35 -0700218 // if we pass in an existing *Native* AudioRecord, we don't need to create/initialize one.
219 if (nativeRecordInJavaObj == 0) {
220 if (jaa == 0) {
221 ALOGE("Error creating AudioRecord: invalid audio attributes");
222 return (jint) AUDIO_JAVA_ERROR;
223 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800224
Paul McLean9b09e532016-01-26 14:43:35 -0700225 if (jSampleRate == 0) {
226 ALOGE("Error creating AudioRecord: invalid sample rates");
227 return (jint) AUDIO_JAVA_ERROR;
228 }
229 jint elements[1];
230 env->GetIntArrayRegion(jSampleRate, 0, 1, elements);
231 int sampleRateInHertz = elements[0];
232
233 // channel index mask takes priority over channel position masks.
234 if (channelIndexMask) {
235 // Java channel index masks need the representation bits set.
236 localChanMask = audio_channel_mask_from_representation_and_bits(
237 AUDIO_CHANNEL_REPRESENTATION_INDEX,
238 channelIndexMask);
239 }
240 // Java channel position masks map directly to the native definition
241
242 if (!audio_is_input_channel(localChanMask)) {
243 ALOGE("Error creating AudioRecord: channel mask %#x is not valid.", localChanMask);
244 return (jint) AUDIORECORD_ERROR_SETUP_INVALIDCHANNELMASK;
245 }
246 uint32_t channelCount = audio_channel_count_from_in_mask(localChanMask);
247
248 // compare the format against the Java constants
249 audio_format_t format = audioFormatToNative(audioFormat);
250 if (format == AUDIO_FORMAT_INVALID) {
251 ALOGE("Error creating AudioRecord: unsupported audio format %d.", audioFormat);
252 return (jint) AUDIORECORD_ERROR_SETUP_INVALIDFORMAT;
253 }
254
255 size_t bytesPerSample = audio_bytes_per_sample(format);
256
257 if (buffSizeInBytes == 0) {
258 ALOGE("Error creating AudioRecord: frameCount is 0.");
259 return (jint) AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT;
260 }
261 size_t frameSize = channelCount * bytesPerSample;
262 size_t frameCount = buffSizeInBytes / frameSize;
263
264 ScopedUtfChars opPackageNameStr(env, opPackageName);
265
266 // create an uninitialized AudioRecord object
267 lpRecorder = new AudioRecord(String16(opPackageNameStr.c_str()));
268
269 // read the AudioAttributes values
270 paa = (audio_attributes_t *) calloc(1, sizeof(audio_attributes_t));
271 const jstring jtags =
272 (jstring) env->GetObjectField(jaa, javaAudioAttrFields.fieldFormattedTags);
273 const char* tags = env->GetStringUTFChars(jtags, NULL);
274 // copying array size -1, char array for tags was calloc'd, no need to NULL-terminate it
275 strncpy(paa->tags, tags, AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1);
276 env->ReleaseStringUTFChars(jtags, tags);
277 paa->source = (audio_source_t) env->GetIntField(jaa, javaAudioAttrFields.fieldRecSource);
278 paa->flags = (audio_flags_mask_t)env->GetIntField(jaa, javaAudioAttrFields.fieldFlags);
279 ALOGV("AudioRecord_setup for source=%d tags=%s flags=%08x", paa->source, paa->tags, paa->flags);
280
281 audio_input_flags_t flags = AUDIO_INPUT_FLAG_NONE;
282 if (paa->flags & AUDIO_FLAG_HW_HOTWORD) {
283 flags = AUDIO_INPUT_FLAG_HW_HOTWORD;
284 }
285 // create the callback information:
286 // this data will be passed with every AudioRecord callback
287 lpCallbackData = new audiorecord_callback_cookie;
288 lpCallbackData->audioRecord_class = (jclass)env->NewGlobalRef(clazz);
289 // we use a weak reference so the AudioRecord object can be garbage collected.
290 lpCallbackData->audioRecord_ref = env->NewGlobalRef(weak_this);
291 lpCallbackData->busy = false;
292
293 const status_t status = lpRecorder->set(paa->source,
294 sampleRateInHertz,
295 format, // word length, PCM
296 localChanMask,
297 frameCount,
298 recorderCallback,// callback_t
299 lpCallbackData,// void* user
300 0, // notificationFrames,
301 true, // threadCanCallJava
302 sessionId,
303 AudioRecord::TRANSFER_DEFAULT,
304 flags,
305 -1, -1, // default uid, pid
306 paa);
307
308 if (status != NO_ERROR) {
309 ALOGE("Error creating AudioRecord instance: initialization check failed with status %d.",
310 status);
311 goto native_init_failure;
312 }
313 } else { // end if nativeRecordInJavaObj == 0)
314 lpRecorder = (AudioRecord*)nativeRecordInJavaObj;
315 // TODO: We need to find out which members of the Java AudioRecord might need to be
316 // initialized from the Native AudioRecord
317 // these are directly returned from getters:
318 // mSampleRate
319 // mRecordSource
320 // mAudioFormat
321 // mChannelMask
322 // mChannelCount
323 // mState (?)
324 // mRecordingState (?)
325 // mPreferredDevice
326
327 // create the callback information:
328 // this data will be passed with every AudioRecord callback
329 lpCallbackData = new audiorecord_callback_cookie;
330 lpCallbackData->audioRecord_class = (jclass)env->NewGlobalRef(clazz);
331 // we use a weak reference so the AudioRecord object can be garbage collected.
332 lpCallbackData->audioRecord_ref = env->NewGlobalRef(weak_this);
333 lpCallbackData->busy = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800334 }
335
Eric Laurent44ff4cd2011-06-18 10:34:05 -0700336 nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
337 if (nSession == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000338 ALOGE("Error creating AudioRecord: Error retrieving session id pointer");
Eric Laurent44ff4cd2011-06-18 10:34:05 -0700339 goto native_init_failure;
340 }
Glenn Kastenb3db2132012-01-19 08:59:58 -0800341 // read the audio session ID back from AudioRecord in case a new session was created during set()
Eric Laurent44ff4cd2011-06-18 10:34:05 -0700342 nSession[0] = lpRecorder->getSessionId();
343 env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
344 nSession = NULL;
345
Glenn Kasten1cbf9b32016-02-02 12:04:09 -0800346 {
347 const jint elements[1] = { (jint) lpRecorder->getSampleRate() };
348 env->SetIntArrayRegion(jSampleRate, 0, 1, elements);
349 }
350
Eric Laurent532bc1c2012-04-20 12:45:03 -0700351 { // scope for the lock
352 Mutex::Autolock l(sLock);
353 sAudioRecordCallBackCookies.add(lpCallbackData);
354 }
Glenn Kasten18db49a2012-03-12 16:29:55 -0700355 // save our newly created C++ AudioRecord in the "nativeRecorderInJavaObj" field
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800356 // of the Java object
Eric Laurent532bc1c2012-04-20 12:45:03 -0700357 setAudioRecord(env, thiz, lpRecorder);
Glenn Kasten18db49a2012-03-12 16:29:55 -0700358
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800359 // save our newly created callback information in the "nativeCallbackCookie" field
360 // of the Java object (in mNativeCallbackCookie) so we can free the memory in finalize()
Ashok Bhat075e9a12014-01-06 13:45:09 +0000361 env->SetLongField(thiz, javaAudioRecordFields.nativeCallbackCookie, (jlong)lpCallbackData);
Glenn Kasten18db49a2012-03-12 16:29:55 -0700362
Eric Laurentbc11a692014-05-16 12:19:25 -0700363 return (jint) AUDIO_JAVA_SUCCESS;
Glenn Kasten18db49a2012-03-12 16:29:55 -0700364
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800365 // failure:
366native_init_failure:
Jean-Michel Trivi4bac5a32009-07-17 12:05:31 -0700367 env->DeleteGlobalRef(lpCallbackData->audioRecord_class);
368 env->DeleteGlobalRef(lpCallbackData->audioRecord_ref);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800369 delete lpCallbackData;
Ashok Bhat075e9a12014-01-06 13:45:09 +0000370 env->SetLongField(thiz, javaAudioRecordFields.nativeCallbackCookie, 0);
Glenn Kasten18db49a2012-03-12 16:29:55 -0700371
Glenn Kasten14d226a2015-05-18 13:53:39 -0700372 // lpRecorder goes out of scope, so reference count drops to zero
Ashok Bhat075e9a12014-01-06 13:45:09 +0000373 return (jint) AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800374}
375
376
377
378// ----------------------------------------------------------------------------
Ashok Bhat075e9a12014-01-06 13:45:09 +0000379static jint
Eric Laurent505e5c82012-03-29 15:19:36 -0700380android_media_AudioRecord_start(JNIEnv *env, jobject thiz, jint event, jint triggerSession)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800381{
Eric Laurent532bc1c2012-04-20 12:45:03 -0700382 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800383 if (lpRecorder == NULL ) {
384 jniThrowException(env, "java/lang/IllegalStateException", NULL);
Eric Laurentbc11a692014-05-16 12:19:25 -0700385 return (jint) AUDIO_JAVA_ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800386 }
Glenn Kasten18db49a2012-03-12 16:29:55 -0700387
Eric Laurentbc11a692014-05-16 12:19:25 -0700388 return nativeToJavaStatus(
Glenn Kasten33b84042016-03-08 12:02:55 -0800389 lpRecorder->start((AudioSystem::sync_event_t)event, (audio_session_t) triggerSession));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800390}
391
392
393// ----------------------------------------------------------------------------
394static void
395android_media_AudioRecord_stop(JNIEnv *env, jobject thiz)
396{
Eric Laurent532bc1c2012-04-20 12:45:03 -0700397 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800398 if (lpRecorder == NULL ) {
399 jniThrowException(env, "java/lang/IllegalStateException", NULL);
400 return;
401 }
402
403 lpRecorder->stop();
Steve Block71f2cf12011-10-20 11:56:00 +0100404 //ALOGV("Called lpRecorder->stop()");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800405}
406
407
408// ----------------------------------------------------------------------------
Dave Sparkse6335c92010-03-13 17:08:22 -0800409
Eric Laurent532bc1c2012-04-20 12:45:03 -0700410#define CALLBACK_COND_WAIT_TIMEOUT_MS 1000
411static void android_media_AudioRecord_release(JNIEnv *env, jobject thiz) {
412 sp<AudioRecord> lpRecorder = setAudioRecord(env, thiz, 0);
413 if (lpRecorder == NULL) {
414 return;
415 }
Glenn Kasten2fbf25b2014-03-28 15:41:58 -0700416 ALOGV("About to delete lpRecorder: %p", lpRecorder.get());
Eric Laurent532bc1c2012-04-20 12:45:03 -0700417 lpRecorder->stop();
418
Ashok Bhat075e9a12014-01-06 13:45:09 +0000419 audiorecord_callback_cookie *lpCookie = (audiorecord_callback_cookie *)env->GetLongField(
Dave Sparkse6335c92010-03-13 17:08:22 -0800420 thiz, javaAudioRecordFields.nativeCallbackCookie);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800421
Dave Sparkse6335c92010-03-13 17:08:22 -0800422 // reset the native resources in the Java object so any attempt to access
423 // them after a call to release fails.
Ashok Bhat075e9a12014-01-06 13:45:09 +0000424 env->SetLongField(thiz, javaAudioRecordFields.nativeCallbackCookie, 0);
Dave Sparkse6335c92010-03-13 17:08:22 -0800425
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800426 // delete the callback information
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800427 if (lpCookie) {
Eric Laurent532bc1c2012-04-20 12:45:03 -0700428 Mutex::Autolock l(sLock);
Glenn Kasten2fbf25b2014-03-28 15:41:58 -0700429 ALOGV("deleting lpCookie: %p", lpCookie);
Eric Laurent532bc1c2012-04-20 12:45:03 -0700430 while (lpCookie->busy) {
431 if (lpCookie->cond.waitRelative(sLock,
432 milliseconds(CALLBACK_COND_WAIT_TIMEOUT_MS)) !=
433 NO_ERROR) {
434 break;
435 }
436 }
437 sAudioRecordCallBackCookies.remove(lpCookie);
Jean-Michel Trivi4bac5a32009-07-17 12:05:31 -0700438 env->DeleteGlobalRef(lpCookie->audioRecord_class);
439 env->DeleteGlobalRef(lpCookie->audioRecord_ref);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800440 delete lpCookie;
441 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800442}
443
444
445// ----------------------------------------------------------------------------
Dave Sparkse6335c92010-03-13 17:08:22 -0800446static void android_media_AudioRecord_finalize(JNIEnv *env, jobject thiz) {
447 android_media_AudioRecord_release(env, thiz);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800448}
449
Andy Hung0e9e2f32015-04-13 23:47:18 -0700450// overloaded JNI array helper functions
451static inline
452jbyte *envGetArrayElements(JNIEnv *env, jbyteArray array, jboolean *isCopy) {
453 return env->GetByteArrayElements(array, isCopy);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800454}
455
Andy Hung0e9e2f32015-04-13 23:47:18 -0700456static inline
457void envReleaseArrayElements(JNIEnv *env, jbyteArray array, jbyte *elems, jint mode) {
458 env->ReleaseByteArrayElements(array, elems, mode);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800459}
460
Andy Hung0e9e2f32015-04-13 23:47:18 -0700461static inline
462jshort *envGetArrayElements(JNIEnv *env, jshortArray array, jboolean *isCopy) {
463 return env->GetShortArrayElements(array, isCopy);
464}
465
466static inline
467void envReleaseArrayElements(JNIEnv *env, jshortArray array, jshort *elems, jint mode) {
468 env->ReleaseShortArrayElements(array, elems, mode);
469}
470
471static inline
472jfloat *envGetArrayElements(JNIEnv *env, jfloatArray array, jboolean *isCopy) {
473 return env->GetFloatArrayElements(array, isCopy);
474}
475
476static inline
477void envReleaseArrayElements(JNIEnv *env, jfloatArray array, jfloat *elems, jint mode) {
478 env->ReleaseFloatArrayElements(array, elems, mode);
479}
480
481static inline
482jint interpretReadSizeError(ssize_t readSize) {
Eric Laurent219de732016-05-23 12:41:50 -0700483 if (readSize == WOULD_BLOCK) {
Andy Hung0e9e2f32015-04-13 23:47:18 -0700484 return (jint)0;
Eric Laurent219de732016-05-23 12:41:50 -0700485 } else if (readSize == NO_INIT) {
486 return AUDIO_JAVA_DEAD_OBJECT;
487 } else {
488 ALOGE("Error %zd during AudioRecord native read", readSize);
489 return nativeToJavaStatus(readSize);
Andy Hung0e9e2f32015-04-13 23:47:18 -0700490 }
491}
492
493template <typename T>
494static jint android_media_AudioRecord_readInArray(JNIEnv *env, jobject thiz,
495 T javaAudioData,
496 jint offsetInSamples, jint sizeInSamples,
497 jboolean isReadBlocking) {
Andy Hung58b0f3f2015-03-27 17:59:45 -0700498 // get the audio recorder from which we'll read new audio samples
499 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
500 if (lpRecorder == NULL) {
501 ALOGE("Unable to retrieve AudioRecord object");
502 return (jint)AUDIO_JAVA_INVALID_OPERATION;
503 }
504
505 if (javaAudioData == NULL) {
Andy Hung0e9e2f32015-04-13 23:47:18 -0700506 ALOGE("Invalid Java array to store recorded audio");
507 return (jint)AUDIO_JAVA_BAD_VALUE;
508 }
Andy Hung58b0f3f2015-03-27 17:59:45 -0700509
Andy Hung58b0f3f2015-03-27 17:59:45 -0700510 // NOTE: We may use GetPrimitiveArrayCritical() when the JNI implementation changes in such
511 // a way that it becomes much more efficient. When doing so, we will have to prevent the
512 // AudioSystem callback to be called while in critical section (in case of media server
513 // process crash for instance)
Andy Hung0e9e2f32015-04-13 23:47:18 -0700514
515 // get the pointer to where we'll record the audio
516 auto *recordBuff = envGetArrayElements(env, javaAudioData, NULL);
Andy Hung58b0f3f2015-03-27 17:59:45 -0700517 if (recordBuff == NULL) {
518 ALOGE("Error retrieving destination for recorded audio data");
519 return (jint)AUDIO_JAVA_BAD_VALUE;
520 }
521
522 // read the new audio data from the native AudioRecord object
Andy Hung0e9e2f32015-04-13 23:47:18 -0700523 const size_t sizeInBytes = sizeInSamples * sizeof(*recordBuff);
524 ssize_t readSize = lpRecorder->read(
525 recordBuff + offsetInSamples, sizeInBytes, isReadBlocking == JNI_TRUE /* blocking */);
Andy Hung58b0f3f2015-03-27 17:59:45 -0700526
Andy Hung0e9e2f32015-04-13 23:47:18 -0700527 envReleaseArrayElements(env, javaAudioData, recordBuff, 0);
Andy Hung58b0f3f2015-03-27 17:59:45 -0700528
529 if (readSize < 0) {
Andy Hung0e9e2f32015-04-13 23:47:18 -0700530 return interpretReadSizeError(readSize);
Andy Hung58b0f3f2015-03-27 17:59:45 -0700531 }
Andy Hung0e9e2f32015-04-13 23:47:18 -0700532 return (jint)(readSize / sizeof(*recordBuff));
Andy Hung58b0f3f2015-03-27 17:59:45 -0700533}
534
535// ----------------------------------------------------------------------------
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800536static jint android_media_AudioRecord_readInDirectBuffer(JNIEnv *env, jobject thiz,
Andy Hung0e9e2f32015-04-13 23:47:18 -0700537 jobject jBuffer, jint sizeInBytes,
538 jboolean isReadBlocking) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800539 // get the audio recorder from which we'll read new audio samples
Eric Laurent532bc1c2012-04-20 12:45:03 -0700540 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
Glenn Kasten18db49a2012-03-12 16:29:55 -0700541 if (lpRecorder==NULL)
Andy Hung0e9e2f32015-04-13 23:47:18 -0700542 return (jint)AUDIO_JAVA_INVALID_OPERATION;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800543
544 // direct buffer and direct access supported?
545 long capacity = env->GetDirectBufferCapacity(jBuffer);
Glenn Kasten18db49a2012-03-12 16:29:55 -0700546 if (capacity == -1) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800547 // buffer direct access is not supported
Steve Block3762c312012-01-06 19:20:56 +0000548 ALOGE("Buffer direct access is not supported, can't record");
Andy Hung0e9e2f32015-04-13 23:47:18 -0700549 return (jint)AUDIO_JAVA_BAD_VALUE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800550 }
Steve Block71f2cf12011-10-20 11:56:00 +0100551 //ALOGV("capacity = %ld", capacity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800552 jbyte* nativeFromJavaBuf = (jbyte*) env->GetDirectBufferAddress(jBuffer);
Glenn Kasten18db49a2012-03-12 16:29:55 -0700553 if (nativeFromJavaBuf==NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000554 ALOGE("Buffer direct access is not supported, can't record");
Andy Hung0e9e2f32015-04-13 23:47:18 -0700555 return (jint)AUDIO_JAVA_BAD_VALUE;
Glenn Kasten18db49a2012-03-12 16:29:55 -0700556 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800557
558 // read new data from the recorder
Eric Laurent357263d2013-09-09 10:31:59 -0700559 ssize_t readSize = lpRecorder->read(nativeFromJavaBuf,
Andy Hung0e9e2f32015-04-13 23:47:18 -0700560 capacity < sizeInBytes ? capacity : sizeInBytes,
561 isReadBlocking == JNI_TRUE /* blocking */);
Eric Laurent357263d2013-09-09 10:31:59 -0700562 if (readSize < 0) {
Andy Hung0e9e2f32015-04-13 23:47:18 -0700563 return interpretReadSizeError(readSize);
Eric Laurent357263d2013-09-09 10:31:59 -0700564 }
565 return (jint)readSize;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800566}
567
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800568// ----------------------------------------------------------------------------
Andy Hunge90a0a82015-05-19 15:44:31 -0700569static jint android_media_AudioRecord_get_buffer_size_in_frames(JNIEnv *env, jobject thiz) {
Andy Hung864ae672015-04-14 11:06:48 -0700570 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
571 if (lpRecorder == NULL) {
572 jniThrowException(env, "java/lang/IllegalStateException",
Andy Hunge90a0a82015-05-19 15:44:31 -0700573 "Unable to retrieve AudioRecord pointer for frameCount()");
Andy Hung864ae672015-04-14 11:06:48 -0700574 return (jint)AUDIO_JAVA_ERROR;
575 }
576 return lpRecorder->frameCount();
577}
578
579// ----------------------------------------------------------------------------
Glenn Kasten18db49a2012-03-12 16:29:55 -0700580static jint android_media_AudioRecord_set_marker_pos(JNIEnv *env, jobject thiz,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800581 jint markerPos) {
Glenn Kasten18db49a2012-03-12 16:29:55 -0700582
Eric Laurent532bc1c2012-04-20 12:45:03 -0700583 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
584 if (lpRecorder == NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800585 jniThrowException(env, "java/lang/IllegalStateException",
586 "Unable to retrieve AudioRecord pointer for setMarkerPosition()");
Eric Laurentbc11a692014-05-16 12:19:25 -0700587 return (jint)AUDIO_JAVA_ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800588 }
Eric Laurentbc11a692014-05-16 12:19:25 -0700589 return nativeToJavaStatus( lpRecorder->setMarkerPosition(markerPos) );
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800590}
591
592
593// ----------------------------------------------------------------------------
594static jint android_media_AudioRecord_get_marker_pos(JNIEnv *env, jobject thiz) {
Glenn Kasten18db49a2012-03-12 16:29:55 -0700595
Eric Laurent532bc1c2012-04-20 12:45:03 -0700596 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800597 uint32_t markerPos = 0;
Glenn Kasten18db49a2012-03-12 16:29:55 -0700598
Eric Laurent532bc1c2012-04-20 12:45:03 -0700599 if (lpRecorder == NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800600 jniThrowException(env, "java/lang/IllegalStateException",
601 "Unable to retrieve AudioRecord pointer for getMarkerPosition()");
Eric Laurentbc11a692014-05-16 12:19:25 -0700602 return (jint)AUDIO_JAVA_ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800603 }
Eric Laurent532bc1c2012-04-20 12:45:03 -0700604 lpRecorder->getMarkerPosition(&markerPos);
605 return (jint)markerPos;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800606}
607
608
609// ----------------------------------------------------------------------------
610static jint android_media_AudioRecord_set_pos_update_period(JNIEnv *env, jobject thiz,
611 jint period) {
Glenn Kasten18db49a2012-03-12 16:29:55 -0700612
Eric Laurent532bc1c2012-04-20 12:45:03 -0700613 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
Glenn Kasten18db49a2012-03-12 16:29:55 -0700614
Eric Laurent532bc1c2012-04-20 12:45:03 -0700615 if (lpRecorder == NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800616 jniThrowException(env, "java/lang/IllegalStateException",
617 "Unable to retrieve AudioRecord pointer for setPositionUpdatePeriod()");
Eric Laurentbc11a692014-05-16 12:19:25 -0700618 return (jint)AUDIO_JAVA_ERROR;
Glenn Kasten18db49a2012-03-12 16:29:55 -0700619 }
Eric Laurentbc11a692014-05-16 12:19:25 -0700620 return nativeToJavaStatus( lpRecorder->setPositionUpdatePeriod(period) );
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800621}
622
623
624// ----------------------------------------------------------------------------
625static jint android_media_AudioRecord_get_pos_update_period(JNIEnv *env, jobject thiz) {
Glenn Kasten18db49a2012-03-12 16:29:55 -0700626
Eric Laurent532bc1c2012-04-20 12:45:03 -0700627 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800628 uint32_t period = 0;
Glenn Kasten18db49a2012-03-12 16:29:55 -0700629
Eric Laurent532bc1c2012-04-20 12:45:03 -0700630 if (lpRecorder == NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800631 jniThrowException(env, "java/lang/IllegalStateException",
632 "Unable to retrieve AudioRecord pointer for getPositionUpdatePeriod()");
Eric Laurentbc11a692014-05-16 12:19:25 -0700633 return (jint)AUDIO_JAVA_ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800634 }
Eric Laurent532bc1c2012-04-20 12:45:03 -0700635 lpRecorder->getPositionUpdatePeriod(&period);
636 return (jint)period;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800637}
638
639
640// ----------------------------------------------------------------------------
641// returns the minimum required size for the successful creation of an AudioRecord instance.
642// returns 0 if the parameter combination is not supported.
643// return -1 if there was an error querying the buffer size.
644static jint android_media_AudioRecord_get_min_buff_size(JNIEnv *env, jobject thiz,
Glenn Kasten5b8fd442013-11-14 09:44:14 -0800645 jint sampleRateInHertz, jint channelCount, jint audioFormat) {
Chia-chi Yehc3308072010-08-19 17:14:36 +0800646
Eric Laurent532bc1c2012-04-20 12:45:03 -0700647 ALOGV(">> android_media_AudioRecord_get_min_buff_size(%d, %d, %d)",
Glenn Kasten5b8fd442013-11-14 09:44:14 -0800648 sampleRateInHertz, channelCount, audioFormat);
Chia-chi Yehc3308072010-08-19 17:14:36 +0800649
Glenn Kastenfd1e3df2012-11-13 15:21:06 -0800650 size_t frameCount = 0;
Glenn Kastena5a42382013-12-19 12:34:56 -0800651 audio_format_t format = audioFormatToNative(audioFormat);
Chia-chi Yehc3308072010-08-19 17:14:36 +0800652 status_t result = AudioRecord::getMinFrameCount(&frameCount,
653 sampleRateInHertz,
Glenn Kastena5a42382013-12-19 12:34:56 -0800654 format,
Glenn Kasten5b8fd442013-11-14 09:44:14 -0800655 audio_channel_in_mask_from_count(channelCount));
Chia-chi Yehc3308072010-08-19 17:14:36 +0800656
657 if (result == BAD_VALUE) {
658 return 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800659 }
Chia-chi Yehc3308072010-08-19 17:14:36 +0800660 if (result != NO_ERROR) {
661 return -1;
662 }
Glenn Kasten5b8fd442013-11-14 09:44:14 -0800663 return frameCount * channelCount * audio_bytes_per_sample(format);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800664}
665
Paul McLean2d6de4c2015-04-17 13:13:28 -0600666static jboolean android_media_AudioRecord_setInputDevice(
667 JNIEnv *env, jobject thiz, jint device_id) {
668
Paul McLean6bd27e12015-04-24 14:01:29 -0600669 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
Eric Laurent4bcdba82015-05-01 11:37:49 -0700670 if (lpRecorder == 0) {
Paul McLeancef696e2015-05-21 08:51:18 -0700671 return false;
Eric Laurent4bcdba82015-05-01 11:37:49 -0700672 }
Paul McLean6bd27e12015-04-24 14:01:29 -0600673 return lpRecorder->setInputDevice(device_id) == NO_ERROR;
Paul McLean2d6de4c2015-04-17 13:13:28 -0600674}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800675
Eric Laurent4bcdba82015-05-01 11:37:49 -0700676static jint android_media_AudioRecord_getRoutedDeviceId(
677 JNIEnv *env, jobject thiz) {
678
679 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
680 if (lpRecorder == 0) {
681 return 0;
682 }
683 return (jint)lpRecorder->getRoutedDeviceId();
684}
685
686static void android_media_AudioRecord_enableDeviceCallback(
687 JNIEnv *env, jobject thiz) {
688
689 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
690 if (lpRecorder == 0) {
691 return;
692 }
693 sp<JNIDeviceCallback> cb = getJniDeviceCallback(env, thiz);
694 if (cb != 0) {
695 return;
696 }
697 audiorecord_callback_cookie *cookie =
698 (audiorecord_callback_cookie *)env->GetLongField(thiz,
699 javaAudioRecordFields.nativeCallbackCookie);
700 if (cookie == NULL) {
701 return;
702 }
703
704 cb = new JNIDeviceCallback(env, thiz, cookie->audioRecord_ref,
705 javaAudioRecordFields.postNativeEventInJava);
706 status_t status = lpRecorder->addAudioDeviceCallback(cb);
707 if (status == NO_ERROR) {
708 setJniDeviceCallback(env, thiz, cb);
709 }
710}
711
712static void android_media_AudioRecord_disableDeviceCallback(
713 JNIEnv *env, jobject thiz) {
714
715 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
716 if (lpRecorder == 0) {
717 return;
718 }
719 sp<JNIDeviceCallback> cb = setJniDeviceCallback(env, thiz, 0);
720 if (cb != 0) {
721 lpRecorder->removeAudioDeviceCallback(cb);
722 }
723}
724
Andy Hung0ad99c02016-01-15 17:53:47 -0800725// ----------------------------------------------------------------------------
726static jint android_media_AudioRecord_get_timestamp(JNIEnv *env, jobject thiz,
727 jobject timestamp, jint timebase) {
728 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
Eric Laurent4bcdba82015-05-01 11:37:49 -0700729
Andy Hung0ad99c02016-01-15 17:53:47 -0800730 if (lpRecorder == NULL) {
731 jniThrowException(env, "java/lang/IllegalStateException",
732 "Unable to retrieve AudioRecord pointer for getTimestamp()");
733 return (jint)AUDIO_JAVA_ERROR;
734 }
735
Andy Hung0ad99c02016-01-15 17:53:47 -0800736 ExtendedTimestamp ts;
Andy Hungf4d81bd2016-01-28 17:47:56 -0800737 jint status = nativeToJavaStatus(lpRecorder->getTimestamp(&ts));
Andy Hung0ad99c02016-01-15 17:53:47 -0800738
739 if (status == AUDIO_JAVA_SUCCESS) {
740 // set the data
741 int64_t position, time;
742
743 status = nativeToJavaStatus(ts.getBestTimestamp(&position, &time, timebase));
744 if (status == AUDIO_JAVA_SUCCESS) {
745 env->SetLongField(
746 timestamp, javaAudioTimestampFields.fieldFramePosition, position);
747 env->SetLongField(
748 timestamp, javaAudioTimestampFields.fieldNanoTime, time);
749 }
750 }
751 return status;
Andy Hung0ad99c02016-01-15 17:53:47 -0800752}
Eric Laurent4bcdba82015-05-01 11:37:49 -0700753
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800754// ----------------------------------------------------------------------------
Ray Essick510225b2018-01-24 14:27:16 -0800755static jobject
756android_media_AudioRecord_native_getMetrics(JNIEnv *env, jobject thiz)
757{
758 ALOGV("android_media_AudioRecord_native_getMetrics");
759
760 sp<AudioRecord> lpRecord = getAudioRecord(env, thiz);
761
762 if (lpRecord == NULL) {
763 ALOGE("Unable to retrieve AudioRecord pointer for getMetrics()");
764 jniThrowException(env, "java/lang/IllegalStateException", NULL);
765 return (jobject) NULL;
766 }
767
768 // get what we have for the metrics from the record session
769 MediaAnalyticsItem *item = NULL;
770
771 status_t err = lpRecord->getMetrics(item);
772 if (err != OK) {
773 ALOGE("getMetrics failed");
774 jniThrowException(env, "java/lang/IllegalStateException", NULL);
775 return (jobject) NULL;
776 }
777
778 jobject mybundle = MediaMetricsJNI::writeMetricsToBundle(env, item, NULL /* mybundle */);
779
780 // housekeeping
781 delete item;
782 item = NULL;
783
784 return mybundle;
785}
786
787// ----------------------------------------------------------------------------
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800788// ----------------------------------------------------------------------------
Daniel Micay76f6a862015-09-19 17:31:01 -0400789static const JNINativeMethod gMethods[] = {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800790 // name, signature, funcPtr
Eric Laurent505e5c82012-03-29 15:19:36 -0700791 {"native_start", "(II)I", (void *)android_media_AudioRecord_start},
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800792 {"native_stop", "()V", (void *)android_media_AudioRecord_stop},
Paul McLean9b09e532016-01-26 14:43:35 -0700793 {"native_setup", "(Ljava/lang/Object;Ljava/lang/Object;[IIIII[ILjava/lang/String;J)I",
794 (void *)android_media_AudioRecord_setup},
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800795 {"native_finalize", "()V", (void *)android_media_AudioRecord_finalize},
796 {"native_release", "()V", (void *)android_media_AudioRecord_release},
Glenn Kasten18db49a2012-03-12 16:29:55 -0700797 {"native_read_in_byte_array",
Andy Hung0e9e2f32015-04-13 23:47:18 -0700798 "([BIIZ)I",
799 (void *)android_media_AudioRecord_readInArray<jbyteArray>},
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800800 {"native_read_in_short_array",
Andy Hung0e9e2f32015-04-13 23:47:18 -0700801 "([SIIZ)I",
802 (void *)android_media_AudioRecord_readInArray<jshortArray>},
Andy Hung58b0f3f2015-03-27 17:59:45 -0700803 {"native_read_in_float_array",
Andy Hung0e9e2f32015-04-13 23:47:18 -0700804 "([FIIZ)I",
805 (void *)android_media_AudioRecord_readInArray<jfloatArray>},
806 {"native_read_in_direct_buffer","(Ljava/lang/Object;IZ)I",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800807 (void *)android_media_AudioRecord_readInDirectBuffer},
Andy Hunge90a0a82015-05-19 15:44:31 -0700808 {"native_get_buffer_size_in_frames",
809 "()I", (void *)android_media_AudioRecord_get_buffer_size_in_frames},
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800810 {"native_set_marker_pos","(I)I", (void *)android_media_AudioRecord_set_marker_pos},
811 {"native_get_marker_pos","()I", (void *)android_media_AudioRecord_get_marker_pos},
812 {"native_set_pos_update_period",
813 "(I)I", (void *)android_media_AudioRecord_set_pos_update_period},
814 {"native_get_pos_update_period",
815 "()I", (void *)android_media_AudioRecord_get_pos_update_period},
816 {"native_get_min_buff_size",
817 "(III)I", (void *)android_media_AudioRecord_get_min_buff_size},
Ray Essick510225b2018-01-24 14:27:16 -0800818 {"native_getMetrics", "()Landroid/os/PersistableBundle;",
819 (void *)android_media_AudioRecord_native_getMetrics},
Paul McLean2d6de4c2015-04-17 13:13:28 -0600820 {"native_setInputDevice", "(I)Z", (void *)android_media_AudioRecord_setInputDevice},
Eric Laurent4bcdba82015-05-01 11:37:49 -0700821 {"native_getRoutedDeviceId", "()I", (void *)android_media_AudioRecord_getRoutedDeviceId},
822 {"native_enableDeviceCallback", "()V", (void *)android_media_AudioRecord_enableDeviceCallback},
823 {"native_disableDeviceCallback", "()V",
824 (void *)android_media_AudioRecord_disableDeviceCallback},
Andy Hung0ad99c02016-01-15 17:53:47 -0800825 {"native_get_timestamp", "(Landroid/media/AudioTimestamp;I)I",
826 (void *)android_media_AudioRecord_get_timestamp},
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800827};
828
829// field names found in android/media/AudioRecord.java
830#define JAVA_POSTEVENT_CALLBACK_NAME "postEventFromNative"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800831#define JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME "mNativeRecorderInJavaObj"
832#define JAVA_NATIVECALLBACKINFO_FIELD_NAME "mNativeCallbackCookie"
Eric Laurent4bcdba82015-05-01 11:37:49 -0700833#define JAVA_NATIVEDEVICECALLBACK_FIELD_NAME "mNativeDeviceCallback"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800834
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800835// ----------------------------------------------------------------------------
836int register_android_media_AudioRecord(JNIEnv *env)
837{
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800838 javaAudioRecordFields.postNativeEventInJava = NULL;
839 javaAudioRecordFields.nativeRecorderInJavaObj = NULL;
840 javaAudioRecordFields.nativeCallbackCookie = NULL;
Eric Laurent4bcdba82015-05-01 11:37:49 -0700841 javaAudioRecordFields.nativeDeviceCallback = NULL;
Glenn Kasten18db49a2012-03-12 16:29:55 -0700842
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800843
844 // Get the AudioRecord class
Andreas Gampeed6b9df2014-11-20 22:02:20 -0800845 jclass audioRecordClass = FindClassOrDie(env, kClassPathName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800846 // Get the postEvent method
Andreas Gampeed6b9df2014-11-20 22:02:20 -0800847 javaAudioRecordFields.postNativeEventInJava = GetStaticMethodIDOrDie(env,
848 audioRecordClass, JAVA_POSTEVENT_CALLBACK_NAME,
849 "(Ljava/lang/Object;IIILjava/lang/Object;)V");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800850
851 // Get the variables
852 // mNativeRecorderInJavaObj
Andreas Gampeed6b9df2014-11-20 22:02:20 -0800853 javaAudioRecordFields.nativeRecorderInJavaObj = GetFieldIDOrDie(env,
854 audioRecordClass, JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME, "J");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800855 // mNativeCallbackCookie
Andreas Gampeed6b9df2014-11-20 22:02:20 -0800856 javaAudioRecordFields.nativeCallbackCookie = GetFieldIDOrDie(env,
857 audioRecordClass, JAVA_NATIVECALLBACKINFO_FIELD_NAME, "J");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800858
Eric Laurent4bcdba82015-05-01 11:37:49 -0700859 javaAudioRecordFields.nativeDeviceCallback = GetFieldIDOrDie(env,
860 audioRecordClass, JAVA_NATIVEDEVICECALLBACK_FIELD_NAME, "J");
861
Jean-Michel Trivi701d6ff2014-07-16 07:51:22 -0700862 // Get the AudioAttributes class and fields
Andreas Gampeed6b9df2014-11-20 22:02:20 -0800863 jclass audioAttrClass = FindClassOrDie(env, kAudioAttributesClassPathName);
864 javaAudioAttrFields.fieldRecSource = GetFieldIDOrDie(env, audioAttrClass, "mSource", "I");
865 javaAudioAttrFields.fieldFlags = GetFieldIDOrDie(env, audioAttrClass, "mFlags", "I");
866 javaAudioAttrFields.fieldFormattedTags = GetFieldIDOrDie(env,
867 audioAttrClass, "mFormattedTags", "Ljava/lang/String;");
Jean-Michel Trivi701d6ff2014-07-16 07:51:22 -0700868
Andy Hung0ad99c02016-01-15 17:53:47 -0800869 // Get the RecordTimestamp class and fields
870 jclass audioTimestampClass = FindClassOrDie(env, "android/media/AudioTimestamp");
871 javaAudioTimestampFields.fieldFramePosition =
872 GetFieldIDOrDie(env, audioTimestampClass, "framePosition", "J");
873 javaAudioTimestampFields.fieldNanoTime =
874 GetFieldIDOrDie(env, audioTimestampClass, "nanoTime", "J");
875
Andreas Gampeed6b9df2014-11-20 22:02:20 -0800876 return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800877}
878
879// ----------------------------------------------------------------------------