blob: 342aba024fc561a825e2fd0dfe3587a23f253a00 [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>
jiabin23675f62018-01-17 18:05:25 -080028#include <media/MicrophoneInfo.h>
29#include <vector>
Dima Zavin24fc2fb2011-04-19 22:30:36 -070030
Steven Moreland2279b252017-07-19 09:50:45 -070031#include <nativehelper/ScopedUtfChars.h>
Svet Ganovfa5ecdc2015-04-28 12:03:28 -070032
Glenn Kastenfe834d32014-01-08 14:49:08 -080033#include "android_media_AudioFormat.h"
Eric Laurentbc11a692014-05-16 12:19:25 -070034#include "android_media_AudioErrors.h"
Eric Laurent4bcdba82015-05-01 11:37:49 -070035#include "android_media_DeviceCallback.h"
Ray Essick510225b2018-01-24 14:27:16 -080036#include "android_media_MediaMetricsJNI.h"
jiabin23675f62018-01-17 18:05:25 -080037#include "android_media_MicrophoneInfo.h"
François Gaffieb4691282018-07-09 13:07:32 +020038#include "android_media_AudioAttributes.h"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080039
40// ----------------------------------------------------------------------------
41
42using namespace android;
43
44// ----------------------------------------------------------------------------
45static const char* const kClassPathName = "android/media/AudioRecord";
46
jiabin23675f62018-01-17 18:05:25 -080047static jclass gArrayListClass;
48static struct {
49 jmethodID add;
50} gArrayListMethods;
51
Jean-Michel Trivi701d6ff2014-07-16 07:51:22 -070052struct audio_record_fields_t {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080053 // these fields provide access from C++ to the...
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080054 jmethodID postNativeEventInJava; //... event post callback method
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080055 jfieldID nativeRecorderInJavaObj; // provides access to the C++ AudioRecord object
56 jfieldID nativeCallbackCookie; // provides access to the AudioRecord callback data
Eric Laurent4bcdba82015-05-01 11:37:49 -070057 jfieldID nativeDeviceCallback; // provides access to the JNIDeviceCallback instance
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080058};
Jean-Michel Trivi701d6ff2014-07-16 07:51:22 -070059static audio_record_fields_t javaAudioRecordFields;
Andy Hung0ad99c02016-01-15 17:53:47 -080060static struct {
61 jfieldID fieldFramePosition; // AudioTimestamp.framePosition
62 jfieldID fieldNanoTime; // AudioTimestamp.nanoTime
63} javaAudioTimestampFields;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080064
65struct audiorecord_callback_cookie {
66 jclass audioRecord_class;
67 jobject audioRecord_ref;
Eric Laurent532bc1c2012-04-20 12:45:03 -070068 bool busy;
69 Condition cond;
70};
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080071
Eric Laurent532bc1c2012-04-20 12:45:03 -070072static Mutex sLock;
73static SortedVector <audiorecord_callback_cookie *> sAudioRecordCallBackCookies;
Dave Sparkse6335c92010-03-13 17:08:22 -080074
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080075// ----------------------------------------------------------------------------
76
Chih-Hung Hsieh0ca16ef2016-05-19 15:14:54 -070077#define AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT (-16)
78#define AUDIORECORD_ERROR_SETUP_INVALIDCHANNELMASK (-17)
79#define AUDIORECORD_ERROR_SETUP_INVALIDFORMAT (-18)
80#define AUDIORECORD_ERROR_SETUP_INVALIDSOURCE (-19)
81#define AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED (-20)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080082
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080083// ----------------------------------------------------------------------------
84static void recorderCallback(int event, void* user, void *info) {
Eric Laurent532bc1c2012-04-20 12:45:03 -070085
86 audiorecord_callback_cookie *callbackInfo = (audiorecord_callback_cookie *)user;
87 {
88 Mutex::Autolock l(sLock);
89 if (sAudioRecordCallBackCookies.indexOf(callbackInfo) < 0) {
90 return;
91 }
92 callbackInfo->busy = true;
93 }
Glenn Kasten18db49a2012-03-12 16:29:55 -070094
Glenn Kasten5b1576c2013-07-18 16:58:19 -070095 switch (event) {
96 case AudioRecord::EVENT_MARKER: {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080097 JNIEnv *env = AndroidRuntime::getJNIEnv();
Glenn Kastena667ff32013-07-22 07:36:34 -070098 if (user != NULL && env != NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080099 env->CallStaticVoidMethod(
Glenn Kasten18db49a2012-03-12 16:29:55 -0700100 callbackInfo->audioRecord_class,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800101 javaAudioRecordFields.postNativeEventInJava,
102 callbackInfo->audioRecord_ref, event, 0,0, NULL);
103 if (env->ExceptionCheck()) {
104 env->ExceptionDescribe();
105 env->ExceptionClear();
106 }
107 }
Glenn Kasten5b1576c2013-07-18 16:58:19 -0700108 } break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800109
Glenn Kasten5b1576c2013-07-18 16:58:19 -0700110 case AudioRecord::EVENT_NEW_POS: {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800111 JNIEnv *env = AndroidRuntime::getJNIEnv();
Glenn Kastena667ff32013-07-22 07:36:34 -0700112 if (user != NULL && env != NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800113 env->CallStaticVoidMethod(
Glenn Kasten18db49a2012-03-12 16:29:55 -0700114 callbackInfo->audioRecord_class,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800115 javaAudioRecordFields.postNativeEventInJava,
116 callbackInfo->audioRecord_ref, event, 0,0, NULL);
117 if (env->ExceptionCheck()) {
118 env->ExceptionDescribe();
119 env->ExceptionClear();
120 }
121 }
Glenn Kasten5b1576c2013-07-18 16:58:19 -0700122 } break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800123 }
Glenn Kasten5b1576c2013-07-18 16:58:19 -0700124
Eric Laurent532bc1c2012-04-20 12:45:03 -0700125 {
126 Mutex::Autolock l(sLock);
127 callbackInfo->busy = false;
128 callbackInfo->cond.broadcast();
129 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800130}
131
Eric Laurent4bcdba82015-05-01 11:37:49 -0700132static sp<JNIDeviceCallback> getJniDeviceCallback(JNIEnv* env, jobject thiz)
133{
134 Mutex::Autolock l(sLock);
135 JNIDeviceCallback* const cb =
136 (JNIDeviceCallback*)env->GetLongField(thiz,
137 javaAudioRecordFields.nativeDeviceCallback);
138 return sp<JNIDeviceCallback>(cb);
139}
140
141static sp<JNIDeviceCallback> setJniDeviceCallback(JNIEnv* env,
142 jobject thiz,
143 const sp<JNIDeviceCallback>& cb)
144{
145 Mutex::Autolock l(sLock);
146 sp<JNIDeviceCallback> old =
147 (JNIDeviceCallback*)env->GetLongField(thiz,
148 javaAudioRecordFields.nativeDeviceCallback);
149 if (cb.get()) {
150 cb->incStrong((void*)setJniDeviceCallback);
151 }
152 if (old != 0) {
153 old->decStrong((void*)setJniDeviceCallback);
154 }
155 env->SetLongField(thiz, javaAudioRecordFields.nativeDeviceCallback, (jlong)cb.get());
156 return old;
157}
158
Eric Laurent532bc1c2012-04-20 12:45:03 -0700159// ----------------------------------------------------------------------------
160static sp<AudioRecord> getAudioRecord(JNIEnv* env, jobject thiz)
161{
162 Mutex::Autolock l(sLock);
163 AudioRecord* const ar =
Ashok Bhat075e9a12014-01-06 13:45:09 +0000164 (AudioRecord*)env->GetLongField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
Eric Laurent532bc1c2012-04-20 12:45:03 -0700165 return sp<AudioRecord>(ar);
166}
167
168static sp<AudioRecord> setAudioRecord(JNIEnv* env, jobject thiz, const sp<AudioRecord>& ar)
169{
170 Mutex::Autolock l(sLock);
171 sp<AudioRecord> old =
Ashok Bhat075e9a12014-01-06 13:45:09 +0000172 (AudioRecord*)env->GetLongField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
Eric Laurent532bc1c2012-04-20 12:45:03 -0700173 if (ar.get()) {
Mathias Agopianb1d90c82013-03-06 17:45:42 -0800174 ar->incStrong((void*)setAudioRecord);
Eric Laurent532bc1c2012-04-20 12:45:03 -0700175 }
176 if (old != 0) {
Mathias Agopianb1d90c82013-03-06 17:45:42 -0800177 old->decStrong((void*)setAudioRecord);
Eric Laurent532bc1c2012-04-20 12:45:03 -0700178 }
Ashok Bhat075e9a12014-01-06 13:45:09 +0000179 env->SetLongField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj, (jlong)ar.get());
Eric Laurent532bc1c2012-04-20 12:45:03 -0700180 return old;
181}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800182
183// ----------------------------------------------------------------------------
Ashok Bhat075e9a12014-01-06 13:45:09 +0000184static jint
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800185android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject weak_this,
Glenn Kasten1cbf9b32016-02-02 12:04:09 -0800186 jobject jaa, jintArray jSampleRate, jint channelMask, jint channelIndexMask,
Paul McLean9b09e532016-01-26 14:43:35 -0700187 jint audioFormat, jint buffSizeInBytes, jintArray jSession, jstring opPackageName,
188 jlong nativeRecordInJavaObj)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800189{
Steve Block71f2cf12011-10-20 11:56:00 +0100190 //ALOGV(">> Entering android_media_AudioRecord_setup");
Paul McLean9b09e532016-01-26 14:43:35 -0700191 //ALOGV("sampleRate=%d, audioFormat=%d, channel mask=%x, buffSizeInBytes=%d "
192 // "nativeRecordInJavaObj=0x%llX",
193 // sampleRateInHertz, audioFormat, channelMask, buffSizeInBytes, nativeRecordInJavaObj);
194 audio_channel_mask_t localChanMask = inChannelMaskToNative(channelMask);
Eric Laurent532bc1c2012-04-20 12:45:03 -0700195
Eric Laurent44ff4cd2011-06-18 10:34:05 -0700196 if (jSession == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000197 ALOGE("Error creating AudioRecord: invalid session ID pointer");
Eric Laurentbc11a692014-05-16 12:19:25 -0700198 return (jint) AUDIO_JAVA_ERROR;
Eric Laurent44ff4cd2011-06-18 10:34:05 -0700199 }
200
201 jint* nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
202 if (nSession == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000203 ALOGE("Error creating AudioRecord: Error retrieving session id pointer");
Eric Laurentbc11a692014-05-16 12:19:25 -0700204 return (jint) AUDIO_JAVA_ERROR;
Eric Laurent44ff4cd2011-06-18 10:34:05 -0700205 }
Glenn Kasten33b84042016-03-08 12:02:55 -0800206 audio_session_t sessionId = (audio_session_t) nSession[0];
Eric Laurent44ff4cd2011-06-18 10:34:05 -0700207 env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
208 nSession = NULL;
209
Paul McLean9b09e532016-01-26 14:43:35 -0700210 sp<AudioRecord> lpRecorder = 0;
211 audiorecord_callback_cookie *lpCallbackData = NULL;
Jean-Michel Trivi701d6ff2014-07-16 07:51:22 -0700212
Paul McLean9b09e532016-01-26 14:43:35 -0700213 jclass clazz = env->GetObjectClass(thiz);
214 if (clazz == NULL) {
215 ALOGE("Can't find %s when setting up callback.", kClassPathName);
216 return (jint) AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED;
Eric Laurentbdad1af2014-09-19 17:43:29 -0700217 }
Glenn Kasten18db49a2012-03-12 16:29:55 -0700218
Paul McLean9b09e532016-01-26 14:43:35 -0700219 // if we pass in an existing *Native* AudioRecord, we don't need to create/initialize one.
220 if (nativeRecordInJavaObj == 0) {
221 if (jaa == 0) {
222 ALOGE("Error creating AudioRecord: invalid audio attributes");
223 return (jint) AUDIO_JAVA_ERROR;
224 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800225
Paul McLean9b09e532016-01-26 14:43:35 -0700226 if (jSampleRate == 0) {
227 ALOGE("Error creating AudioRecord: invalid sample rates");
228 return (jint) AUDIO_JAVA_ERROR;
229 }
230 jint elements[1];
231 env->GetIntArrayRegion(jSampleRate, 0, 1, elements);
232 int sampleRateInHertz = elements[0];
233
234 // channel index mask takes priority over channel position masks.
235 if (channelIndexMask) {
236 // Java channel index masks need the representation bits set.
237 localChanMask = audio_channel_mask_from_representation_and_bits(
238 AUDIO_CHANNEL_REPRESENTATION_INDEX,
239 channelIndexMask);
240 }
241 // Java channel position masks map directly to the native definition
242
243 if (!audio_is_input_channel(localChanMask)) {
244 ALOGE("Error creating AudioRecord: channel mask %#x is not valid.", localChanMask);
245 return (jint) AUDIORECORD_ERROR_SETUP_INVALIDCHANNELMASK;
246 }
247 uint32_t channelCount = audio_channel_count_from_in_mask(localChanMask);
248
249 // compare the format against the Java constants
250 audio_format_t format = audioFormatToNative(audioFormat);
251 if (format == AUDIO_FORMAT_INVALID) {
252 ALOGE("Error creating AudioRecord: unsupported audio format %d.", audioFormat);
253 return (jint) AUDIORECORD_ERROR_SETUP_INVALIDFORMAT;
254 }
255
256 size_t bytesPerSample = audio_bytes_per_sample(format);
257
258 if (buffSizeInBytes == 0) {
259 ALOGE("Error creating AudioRecord: frameCount is 0.");
260 return (jint) AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT;
261 }
262 size_t frameSize = channelCount * bytesPerSample;
263 size_t frameCount = buffSizeInBytes / frameSize;
264
265 ScopedUtfChars opPackageNameStr(env, opPackageName);
266
267 // create an uninitialized AudioRecord object
268 lpRecorder = new AudioRecord(String16(opPackageNameStr.c_str()));
269
270 // read the AudioAttributes values
François Gaffieb4691282018-07-09 13:07:32 +0200271 auto paa = JNIAudioAttributeHelper::makeUnique();
272 jint jStatus = JNIAudioAttributeHelper::nativeFromJava(env, jaa, paa.get());
273 if (jStatus != (jint)AUDIO_JAVA_SUCCESS) {
274 return jStatus;
275 }
Paul McLean9b09e532016-01-26 14:43:35 -0700276 ALOGV("AudioRecord_setup for source=%d tags=%s flags=%08x", paa->source, paa->tags, paa->flags);
277
278 audio_input_flags_t flags = AUDIO_INPUT_FLAG_NONE;
279 if (paa->flags & AUDIO_FLAG_HW_HOTWORD) {
280 flags = AUDIO_INPUT_FLAG_HW_HOTWORD;
281 }
282 // create the callback information:
283 // this data will be passed with every AudioRecord callback
284 lpCallbackData = new audiorecord_callback_cookie;
285 lpCallbackData->audioRecord_class = (jclass)env->NewGlobalRef(clazz);
286 // we use a weak reference so the AudioRecord object can be garbage collected.
287 lpCallbackData->audioRecord_ref = env->NewGlobalRef(weak_this);
288 lpCallbackData->busy = false;
289
290 const status_t status = lpRecorder->set(paa->source,
291 sampleRateInHertz,
292 format, // word length, PCM
293 localChanMask,
294 frameCount,
295 recorderCallback,// callback_t
296 lpCallbackData,// void* user
297 0, // notificationFrames,
298 true, // threadCanCallJava
299 sessionId,
300 AudioRecord::TRANSFER_DEFAULT,
301 flags,
302 -1, -1, // default uid, pid
François Gaffieb4691282018-07-09 13:07:32 +0200303 paa.get());
Paul McLean9b09e532016-01-26 14:43:35 -0700304
305 if (status != NO_ERROR) {
306 ALOGE("Error creating AudioRecord instance: initialization check failed with status %d.",
307 status);
308 goto native_init_failure;
309 }
310 } else { // end if nativeRecordInJavaObj == 0)
311 lpRecorder = (AudioRecord*)nativeRecordInJavaObj;
312 // TODO: We need to find out which members of the Java AudioRecord might need to be
313 // initialized from the Native AudioRecord
314 // these are directly returned from getters:
315 // mSampleRate
316 // mRecordSource
317 // mAudioFormat
318 // mChannelMask
319 // mChannelCount
320 // mState (?)
321 // mRecordingState (?)
322 // mPreferredDevice
323
324 // create the callback information:
325 // this data will be passed with every AudioRecord callback
326 lpCallbackData = new audiorecord_callback_cookie;
327 lpCallbackData->audioRecord_class = (jclass)env->NewGlobalRef(clazz);
328 // we use a weak reference so the AudioRecord object can be garbage collected.
329 lpCallbackData->audioRecord_ref = env->NewGlobalRef(weak_this);
330 lpCallbackData->busy = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800331 }
332
Eric Laurent44ff4cd2011-06-18 10:34:05 -0700333 nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
334 if (nSession == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000335 ALOGE("Error creating AudioRecord: Error retrieving session id pointer");
Eric Laurent44ff4cd2011-06-18 10:34:05 -0700336 goto native_init_failure;
337 }
Glenn Kastenb3db2132012-01-19 08:59:58 -0800338 // read the audio session ID back from AudioRecord in case a new session was created during set()
Eric Laurent44ff4cd2011-06-18 10:34:05 -0700339 nSession[0] = lpRecorder->getSessionId();
340 env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
341 nSession = NULL;
342
Glenn Kasten1cbf9b32016-02-02 12:04:09 -0800343 {
344 const jint elements[1] = { (jint) lpRecorder->getSampleRate() };
345 env->SetIntArrayRegion(jSampleRate, 0, 1, elements);
346 }
347
Eric Laurent532bc1c2012-04-20 12:45:03 -0700348 { // scope for the lock
349 Mutex::Autolock l(sLock);
350 sAudioRecordCallBackCookies.add(lpCallbackData);
351 }
Glenn Kasten18db49a2012-03-12 16:29:55 -0700352 // save our newly created C++ AudioRecord in the "nativeRecorderInJavaObj" field
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800353 // of the Java object
Eric Laurent532bc1c2012-04-20 12:45:03 -0700354 setAudioRecord(env, thiz, lpRecorder);
Glenn Kasten18db49a2012-03-12 16:29:55 -0700355
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800356 // save our newly created callback information in the "nativeCallbackCookie" field
357 // of the Java object (in mNativeCallbackCookie) so we can free the memory in finalize()
Ashok Bhat075e9a12014-01-06 13:45:09 +0000358 env->SetLongField(thiz, javaAudioRecordFields.nativeCallbackCookie, (jlong)lpCallbackData);
Glenn Kasten18db49a2012-03-12 16:29:55 -0700359
Eric Laurentbc11a692014-05-16 12:19:25 -0700360 return (jint) AUDIO_JAVA_SUCCESS;
Glenn Kasten18db49a2012-03-12 16:29:55 -0700361
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800362 // failure:
363native_init_failure:
Jean-Michel Trivi4bac5a32009-07-17 12:05:31 -0700364 env->DeleteGlobalRef(lpCallbackData->audioRecord_class);
365 env->DeleteGlobalRef(lpCallbackData->audioRecord_ref);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800366 delete lpCallbackData;
Ashok Bhat075e9a12014-01-06 13:45:09 +0000367 env->SetLongField(thiz, javaAudioRecordFields.nativeCallbackCookie, 0);
Glenn Kasten18db49a2012-03-12 16:29:55 -0700368
Glenn Kasten14d226a2015-05-18 13:53:39 -0700369 // lpRecorder goes out of scope, so reference count drops to zero
Ashok Bhat075e9a12014-01-06 13:45:09 +0000370 return (jint) AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800371}
372
373
374
375// ----------------------------------------------------------------------------
Ashok Bhat075e9a12014-01-06 13:45:09 +0000376static jint
Eric Laurent505e5c82012-03-29 15:19:36 -0700377android_media_AudioRecord_start(JNIEnv *env, jobject thiz, jint event, jint triggerSession)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800378{
Eric Laurent532bc1c2012-04-20 12:45:03 -0700379 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800380 if (lpRecorder == NULL ) {
381 jniThrowException(env, "java/lang/IllegalStateException", NULL);
Eric Laurentbc11a692014-05-16 12:19:25 -0700382 return (jint) AUDIO_JAVA_ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800383 }
Glenn Kasten18db49a2012-03-12 16:29:55 -0700384
Eric Laurentbc11a692014-05-16 12:19:25 -0700385 return nativeToJavaStatus(
Glenn Kasten33b84042016-03-08 12:02:55 -0800386 lpRecorder->start((AudioSystem::sync_event_t)event, (audio_session_t) triggerSession));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800387}
388
389
390// ----------------------------------------------------------------------------
391static void
392android_media_AudioRecord_stop(JNIEnv *env, jobject thiz)
393{
Eric Laurent532bc1c2012-04-20 12:45:03 -0700394 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800395 if (lpRecorder == NULL ) {
396 jniThrowException(env, "java/lang/IllegalStateException", NULL);
397 return;
398 }
399
400 lpRecorder->stop();
Steve Block71f2cf12011-10-20 11:56:00 +0100401 //ALOGV("Called lpRecorder->stop()");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800402}
403
404
405// ----------------------------------------------------------------------------
Dave Sparkse6335c92010-03-13 17:08:22 -0800406
Eric Laurent532bc1c2012-04-20 12:45:03 -0700407#define CALLBACK_COND_WAIT_TIMEOUT_MS 1000
408static void android_media_AudioRecord_release(JNIEnv *env, jobject thiz) {
409 sp<AudioRecord> lpRecorder = setAudioRecord(env, thiz, 0);
410 if (lpRecorder == NULL) {
411 return;
412 }
Glenn Kasten2fbf25b2014-03-28 15:41:58 -0700413 ALOGV("About to delete lpRecorder: %p", lpRecorder.get());
Eric Laurent532bc1c2012-04-20 12:45:03 -0700414 lpRecorder->stop();
415
Ashok Bhat075e9a12014-01-06 13:45:09 +0000416 audiorecord_callback_cookie *lpCookie = (audiorecord_callback_cookie *)env->GetLongField(
Dave Sparkse6335c92010-03-13 17:08:22 -0800417 thiz, javaAudioRecordFields.nativeCallbackCookie);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800418
Dave Sparkse6335c92010-03-13 17:08:22 -0800419 // reset the native resources in the Java object so any attempt to access
420 // them after a call to release fails.
Ashok Bhat075e9a12014-01-06 13:45:09 +0000421 env->SetLongField(thiz, javaAudioRecordFields.nativeCallbackCookie, 0);
Dave Sparkse6335c92010-03-13 17:08:22 -0800422
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800423 // delete the callback information
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800424 if (lpCookie) {
Eric Laurent532bc1c2012-04-20 12:45:03 -0700425 Mutex::Autolock l(sLock);
Glenn Kasten2fbf25b2014-03-28 15:41:58 -0700426 ALOGV("deleting lpCookie: %p", lpCookie);
Eric Laurent532bc1c2012-04-20 12:45:03 -0700427 while (lpCookie->busy) {
428 if (lpCookie->cond.waitRelative(sLock,
429 milliseconds(CALLBACK_COND_WAIT_TIMEOUT_MS)) !=
430 NO_ERROR) {
431 break;
432 }
433 }
434 sAudioRecordCallBackCookies.remove(lpCookie);
Jean-Michel Trivi4bac5a32009-07-17 12:05:31 -0700435 env->DeleteGlobalRef(lpCookie->audioRecord_class);
436 env->DeleteGlobalRef(lpCookie->audioRecord_ref);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800437 delete lpCookie;
438 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800439}
440
441
442// ----------------------------------------------------------------------------
Dave Sparkse6335c92010-03-13 17:08:22 -0800443static void android_media_AudioRecord_finalize(JNIEnv *env, jobject thiz) {
444 android_media_AudioRecord_release(env, thiz);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800445}
446
Andy Hung0e9e2f32015-04-13 23:47:18 -0700447// overloaded JNI array helper functions
448static inline
449jbyte *envGetArrayElements(JNIEnv *env, jbyteArray array, jboolean *isCopy) {
450 return env->GetByteArrayElements(array, isCopy);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800451}
452
Andy Hung0e9e2f32015-04-13 23:47:18 -0700453static inline
454void envReleaseArrayElements(JNIEnv *env, jbyteArray array, jbyte *elems, jint mode) {
455 env->ReleaseByteArrayElements(array, elems, mode);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800456}
457
Andy Hung0e9e2f32015-04-13 23:47:18 -0700458static inline
459jshort *envGetArrayElements(JNIEnv *env, jshortArray array, jboolean *isCopy) {
460 return env->GetShortArrayElements(array, isCopy);
461}
462
463static inline
464void envReleaseArrayElements(JNIEnv *env, jshortArray array, jshort *elems, jint mode) {
465 env->ReleaseShortArrayElements(array, elems, mode);
466}
467
468static inline
469jfloat *envGetArrayElements(JNIEnv *env, jfloatArray array, jboolean *isCopy) {
470 return env->GetFloatArrayElements(array, isCopy);
471}
472
473static inline
474void envReleaseArrayElements(JNIEnv *env, jfloatArray array, jfloat *elems, jint mode) {
475 env->ReleaseFloatArrayElements(array, elems, mode);
476}
477
478static inline
479jint interpretReadSizeError(ssize_t readSize) {
Eric Laurent219de732016-05-23 12:41:50 -0700480 if (readSize == WOULD_BLOCK) {
Andy Hung0e9e2f32015-04-13 23:47:18 -0700481 return (jint)0;
Eric Laurent219de732016-05-23 12:41:50 -0700482 } else if (readSize == NO_INIT) {
483 return AUDIO_JAVA_DEAD_OBJECT;
484 } else {
485 ALOGE("Error %zd during AudioRecord native read", readSize);
486 return nativeToJavaStatus(readSize);
Andy Hung0e9e2f32015-04-13 23:47:18 -0700487 }
488}
489
490template <typename T>
491static jint android_media_AudioRecord_readInArray(JNIEnv *env, jobject thiz,
492 T javaAudioData,
493 jint offsetInSamples, jint sizeInSamples,
494 jboolean isReadBlocking) {
Andy Hung58b0f3f2015-03-27 17:59:45 -0700495 // get the audio recorder from which we'll read new audio samples
496 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
497 if (lpRecorder == NULL) {
498 ALOGE("Unable to retrieve AudioRecord object");
499 return (jint)AUDIO_JAVA_INVALID_OPERATION;
500 }
501
502 if (javaAudioData == NULL) {
Andy Hung0e9e2f32015-04-13 23:47:18 -0700503 ALOGE("Invalid Java array to store recorded audio");
504 return (jint)AUDIO_JAVA_BAD_VALUE;
505 }
Andy Hung58b0f3f2015-03-27 17:59:45 -0700506
Andy Hung58b0f3f2015-03-27 17:59:45 -0700507 // NOTE: We may use GetPrimitiveArrayCritical() when the JNI implementation changes in such
508 // a way that it becomes much more efficient. When doing so, we will have to prevent the
509 // AudioSystem callback to be called while in critical section (in case of media server
510 // process crash for instance)
Andy Hung0e9e2f32015-04-13 23:47:18 -0700511
512 // get the pointer to where we'll record the audio
513 auto *recordBuff = envGetArrayElements(env, javaAudioData, NULL);
Andy Hung58b0f3f2015-03-27 17:59:45 -0700514 if (recordBuff == NULL) {
515 ALOGE("Error retrieving destination for recorded audio data");
516 return (jint)AUDIO_JAVA_BAD_VALUE;
517 }
518
519 // read the new audio data from the native AudioRecord object
Andy Hung0e9e2f32015-04-13 23:47:18 -0700520 const size_t sizeInBytes = sizeInSamples * sizeof(*recordBuff);
521 ssize_t readSize = lpRecorder->read(
522 recordBuff + offsetInSamples, sizeInBytes, isReadBlocking == JNI_TRUE /* blocking */);
Andy Hung58b0f3f2015-03-27 17:59:45 -0700523
Andy Hung0e9e2f32015-04-13 23:47:18 -0700524 envReleaseArrayElements(env, javaAudioData, recordBuff, 0);
Andy Hung58b0f3f2015-03-27 17:59:45 -0700525
526 if (readSize < 0) {
Andy Hung0e9e2f32015-04-13 23:47:18 -0700527 return interpretReadSizeError(readSize);
Andy Hung58b0f3f2015-03-27 17:59:45 -0700528 }
Andy Hung0e9e2f32015-04-13 23:47:18 -0700529 return (jint)(readSize / sizeof(*recordBuff));
Andy Hung58b0f3f2015-03-27 17:59:45 -0700530}
531
532// ----------------------------------------------------------------------------
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800533static jint android_media_AudioRecord_readInDirectBuffer(JNIEnv *env, jobject thiz,
Andy Hung0e9e2f32015-04-13 23:47:18 -0700534 jobject jBuffer, jint sizeInBytes,
535 jboolean isReadBlocking) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800536 // get the audio recorder from which we'll read new audio samples
Eric Laurent532bc1c2012-04-20 12:45:03 -0700537 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
Glenn Kasten18db49a2012-03-12 16:29:55 -0700538 if (lpRecorder==NULL)
Andy Hung0e9e2f32015-04-13 23:47:18 -0700539 return (jint)AUDIO_JAVA_INVALID_OPERATION;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800540
541 // direct buffer and direct access supported?
542 long capacity = env->GetDirectBufferCapacity(jBuffer);
Glenn Kasten18db49a2012-03-12 16:29:55 -0700543 if (capacity == -1) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800544 // buffer direct access is not supported
Steve Block3762c312012-01-06 19:20:56 +0000545 ALOGE("Buffer direct access is not supported, can't record");
Andy Hung0e9e2f32015-04-13 23:47:18 -0700546 return (jint)AUDIO_JAVA_BAD_VALUE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800547 }
Steve Block71f2cf12011-10-20 11:56:00 +0100548 //ALOGV("capacity = %ld", capacity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800549 jbyte* nativeFromJavaBuf = (jbyte*) env->GetDirectBufferAddress(jBuffer);
Glenn Kasten18db49a2012-03-12 16:29:55 -0700550 if (nativeFromJavaBuf==NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000551 ALOGE("Buffer direct access is not supported, can't record");
Andy Hung0e9e2f32015-04-13 23:47:18 -0700552 return (jint)AUDIO_JAVA_BAD_VALUE;
Glenn Kasten18db49a2012-03-12 16:29:55 -0700553 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800554
555 // read new data from the recorder
Eric Laurent357263d2013-09-09 10:31:59 -0700556 ssize_t readSize = lpRecorder->read(nativeFromJavaBuf,
Andy Hung0e9e2f32015-04-13 23:47:18 -0700557 capacity < sizeInBytes ? capacity : sizeInBytes,
558 isReadBlocking == JNI_TRUE /* blocking */);
Eric Laurent357263d2013-09-09 10:31:59 -0700559 if (readSize < 0) {
Andy Hung0e9e2f32015-04-13 23:47:18 -0700560 return interpretReadSizeError(readSize);
Eric Laurent357263d2013-09-09 10:31:59 -0700561 }
562 return (jint)readSize;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800563}
564
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800565// ----------------------------------------------------------------------------
Andy Hunge90a0a82015-05-19 15:44:31 -0700566static jint android_media_AudioRecord_get_buffer_size_in_frames(JNIEnv *env, jobject thiz) {
Andy Hung864ae672015-04-14 11:06:48 -0700567 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
568 if (lpRecorder == NULL) {
569 jniThrowException(env, "java/lang/IllegalStateException",
Andy Hunge90a0a82015-05-19 15:44:31 -0700570 "Unable to retrieve AudioRecord pointer for frameCount()");
Andy Hung864ae672015-04-14 11:06:48 -0700571 return (jint)AUDIO_JAVA_ERROR;
572 }
573 return lpRecorder->frameCount();
574}
575
576// ----------------------------------------------------------------------------
Glenn Kasten18db49a2012-03-12 16:29:55 -0700577static jint android_media_AudioRecord_set_marker_pos(JNIEnv *env, jobject thiz,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800578 jint markerPos) {
Glenn Kasten18db49a2012-03-12 16:29:55 -0700579
Eric Laurent532bc1c2012-04-20 12:45:03 -0700580 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
581 if (lpRecorder == NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800582 jniThrowException(env, "java/lang/IllegalStateException",
583 "Unable to retrieve AudioRecord pointer for setMarkerPosition()");
Eric Laurentbc11a692014-05-16 12:19:25 -0700584 return (jint)AUDIO_JAVA_ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800585 }
Eric Laurentbc11a692014-05-16 12:19:25 -0700586 return nativeToJavaStatus( lpRecorder->setMarkerPosition(markerPos) );
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800587}
588
589
590// ----------------------------------------------------------------------------
591static jint android_media_AudioRecord_get_marker_pos(JNIEnv *env, jobject thiz) {
Glenn Kasten18db49a2012-03-12 16:29:55 -0700592
Eric Laurent532bc1c2012-04-20 12:45:03 -0700593 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800594 uint32_t markerPos = 0;
Glenn Kasten18db49a2012-03-12 16:29:55 -0700595
Eric Laurent532bc1c2012-04-20 12:45:03 -0700596 if (lpRecorder == NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800597 jniThrowException(env, "java/lang/IllegalStateException",
598 "Unable to retrieve AudioRecord pointer for getMarkerPosition()");
Eric Laurentbc11a692014-05-16 12:19:25 -0700599 return (jint)AUDIO_JAVA_ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800600 }
Eric Laurent532bc1c2012-04-20 12:45:03 -0700601 lpRecorder->getMarkerPosition(&markerPos);
602 return (jint)markerPos;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800603}
604
605
606// ----------------------------------------------------------------------------
607static jint android_media_AudioRecord_set_pos_update_period(JNIEnv *env, jobject thiz,
608 jint period) {
Glenn Kasten18db49a2012-03-12 16:29:55 -0700609
Eric Laurent532bc1c2012-04-20 12:45:03 -0700610 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
Glenn Kasten18db49a2012-03-12 16:29:55 -0700611
Eric Laurent532bc1c2012-04-20 12:45:03 -0700612 if (lpRecorder == NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800613 jniThrowException(env, "java/lang/IllegalStateException",
614 "Unable to retrieve AudioRecord pointer for setPositionUpdatePeriod()");
Eric Laurentbc11a692014-05-16 12:19:25 -0700615 return (jint)AUDIO_JAVA_ERROR;
Glenn Kasten18db49a2012-03-12 16:29:55 -0700616 }
Eric Laurentbc11a692014-05-16 12:19:25 -0700617 return nativeToJavaStatus( lpRecorder->setPositionUpdatePeriod(period) );
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800618}
619
620
621// ----------------------------------------------------------------------------
622static jint android_media_AudioRecord_get_pos_update_period(JNIEnv *env, jobject thiz) {
Glenn Kasten18db49a2012-03-12 16:29:55 -0700623
Eric Laurent532bc1c2012-04-20 12:45:03 -0700624 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800625 uint32_t period = 0;
Glenn Kasten18db49a2012-03-12 16:29:55 -0700626
Eric Laurent532bc1c2012-04-20 12:45:03 -0700627 if (lpRecorder == NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800628 jniThrowException(env, "java/lang/IllegalStateException",
629 "Unable to retrieve AudioRecord pointer for getPositionUpdatePeriod()");
Eric Laurentbc11a692014-05-16 12:19:25 -0700630 return (jint)AUDIO_JAVA_ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800631 }
Eric Laurent532bc1c2012-04-20 12:45:03 -0700632 lpRecorder->getPositionUpdatePeriod(&period);
633 return (jint)period;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800634}
635
636
637// ----------------------------------------------------------------------------
638// returns the minimum required size for the successful creation of an AudioRecord instance.
639// returns 0 if the parameter combination is not supported.
640// return -1 if there was an error querying the buffer size.
641static jint android_media_AudioRecord_get_min_buff_size(JNIEnv *env, jobject thiz,
Glenn Kasten5b8fd442013-11-14 09:44:14 -0800642 jint sampleRateInHertz, jint channelCount, jint audioFormat) {
Chia-chi Yehc3308072010-08-19 17:14:36 +0800643
Eric Laurent532bc1c2012-04-20 12:45:03 -0700644 ALOGV(">> android_media_AudioRecord_get_min_buff_size(%d, %d, %d)",
Glenn Kasten5b8fd442013-11-14 09:44:14 -0800645 sampleRateInHertz, channelCount, audioFormat);
Chia-chi Yehc3308072010-08-19 17:14:36 +0800646
Glenn Kastenfd1e3df2012-11-13 15:21:06 -0800647 size_t frameCount = 0;
Glenn Kastena5a42382013-12-19 12:34:56 -0800648 audio_format_t format = audioFormatToNative(audioFormat);
Chia-chi Yehc3308072010-08-19 17:14:36 +0800649 status_t result = AudioRecord::getMinFrameCount(&frameCount,
650 sampleRateInHertz,
Glenn Kastena5a42382013-12-19 12:34:56 -0800651 format,
Glenn Kasten5b8fd442013-11-14 09:44:14 -0800652 audio_channel_in_mask_from_count(channelCount));
Chia-chi Yehc3308072010-08-19 17:14:36 +0800653
654 if (result == BAD_VALUE) {
655 return 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800656 }
Chia-chi Yehc3308072010-08-19 17:14:36 +0800657 if (result != NO_ERROR) {
658 return -1;
659 }
Glenn Kasten5b8fd442013-11-14 09:44:14 -0800660 return frameCount * channelCount * audio_bytes_per_sample(format);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800661}
662
Paul McLean2d6de4c2015-04-17 13:13:28 -0600663static jboolean android_media_AudioRecord_setInputDevice(
664 JNIEnv *env, jobject thiz, jint device_id) {
665
Paul McLean6bd27e12015-04-24 14:01:29 -0600666 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
Eric Laurent4bcdba82015-05-01 11:37:49 -0700667 if (lpRecorder == 0) {
Paul McLeancef696e2015-05-21 08:51:18 -0700668 return false;
Eric Laurent4bcdba82015-05-01 11:37:49 -0700669 }
Paul McLean6bd27e12015-04-24 14:01:29 -0600670 return lpRecorder->setInputDevice(device_id) == NO_ERROR;
Paul McLean2d6de4c2015-04-17 13:13:28 -0600671}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800672
Eric Laurent4bcdba82015-05-01 11:37:49 -0700673static jint android_media_AudioRecord_getRoutedDeviceId(
674 JNIEnv *env, jobject thiz) {
675
676 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
677 if (lpRecorder == 0) {
678 return 0;
679 }
680 return (jint)lpRecorder->getRoutedDeviceId();
681}
682
683static void android_media_AudioRecord_enableDeviceCallback(
684 JNIEnv *env, jobject thiz) {
685
686 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
687 if (lpRecorder == 0) {
688 return;
689 }
690 sp<JNIDeviceCallback> cb = getJniDeviceCallback(env, thiz);
691 if (cb != 0) {
692 return;
693 }
694 audiorecord_callback_cookie *cookie =
695 (audiorecord_callback_cookie *)env->GetLongField(thiz,
696 javaAudioRecordFields.nativeCallbackCookie);
697 if (cookie == NULL) {
698 return;
699 }
700
701 cb = new JNIDeviceCallback(env, thiz, cookie->audioRecord_ref,
702 javaAudioRecordFields.postNativeEventInJava);
703 status_t status = lpRecorder->addAudioDeviceCallback(cb);
704 if (status == NO_ERROR) {
705 setJniDeviceCallback(env, thiz, cb);
706 }
707}
708
709static void android_media_AudioRecord_disableDeviceCallback(
710 JNIEnv *env, jobject thiz) {
711
712 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
713 if (lpRecorder == 0) {
714 return;
715 }
716 sp<JNIDeviceCallback> cb = setJniDeviceCallback(env, thiz, 0);
717 if (cb != 0) {
718 lpRecorder->removeAudioDeviceCallback(cb);
719 }
720}
721
Andy Hung0ad99c02016-01-15 17:53:47 -0800722// ----------------------------------------------------------------------------
723static jint android_media_AudioRecord_get_timestamp(JNIEnv *env, jobject thiz,
724 jobject timestamp, jint timebase) {
725 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
Eric Laurent4bcdba82015-05-01 11:37:49 -0700726
Andy Hung0ad99c02016-01-15 17:53:47 -0800727 if (lpRecorder == NULL) {
728 jniThrowException(env, "java/lang/IllegalStateException",
729 "Unable to retrieve AudioRecord pointer for getTimestamp()");
730 return (jint)AUDIO_JAVA_ERROR;
731 }
732
Andy Hung0ad99c02016-01-15 17:53:47 -0800733 ExtendedTimestamp ts;
Andy Hungf4d81bd2016-01-28 17:47:56 -0800734 jint status = nativeToJavaStatus(lpRecorder->getTimestamp(&ts));
Andy Hung0ad99c02016-01-15 17:53:47 -0800735
736 if (status == AUDIO_JAVA_SUCCESS) {
737 // set the data
738 int64_t position, time;
739
740 status = nativeToJavaStatus(ts.getBestTimestamp(&position, &time, timebase));
741 if (status == AUDIO_JAVA_SUCCESS) {
742 env->SetLongField(
743 timestamp, javaAudioTimestampFields.fieldFramePosition, position);
744 env->SetLongField(
745 timestamp, javaAudioTimestampFields.fieldNanoTime, time);
746 }
747 }
748 return status;
Andy Hung0ad99c02016-01-15 17:53:47 -0800749}
Eric Laurent4bcdba82015-05-01 11:37:49 -0700750
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800751// ----------------------------------------------------------------------------
Ray Essick510225b2018-01-24 14:27:16 -0800752static jobject
753android_media_AudioRecord_native_getMetrics(JNIEnv *env, jobject thiz)
754{
755 ALOGV("android_media_AudioRecord_native_getMetrics");
756
757 sp<AudioRecord> lpRecord = getAudioRecord(env, thiz);
758
759 if (lpRecord == NULL) {
760 ALOGE("Unable to retrieve AudioRecord pointer for getMetrics()");
761 jniThrowException(env, "java/lang/IllegalStateException", NULL);
762 return (jobject) NULL;
763 }
764
765 // get what we have for the metrics from the record session
766 MediaAnalyticsItem *item = NULL;
767
768 status_t err = lpRecord->getMetrics(item);
769 if (err != OK) {
770 ALOGE("getMetrics failed");
771 jniThrowException(env, "java/lang/IllegalStateException", NULL);
772 return (jobject) NULL;
773 }
774
775 jobject mybundle = MediaMetricsJNI::writeMetricsToBundle(env, item, NULL /* mybundle */);
776
777 // housekeeping
778 delete item;
779 item = NULL;
780
781 return mybundle;
782}
783
784// ----------------------------------------------------------------------------
jiabin23675f62018-01-17 18:05:25 -0800785static jint android_media_AudioRecord_get_active_microphones(JNIEnv *env,
786 jobject thiz, jobject jActiveMicrophones) {
787 if (jActiveMicrophones == NULL) {
788 ALOGE("jActiveMicrophones is null");
789 return (jint)AUDIO_JAVA_BAD_VALUE;
790 }
791 if (!env->IsInstanceOf(jActiveMicrophones, gArrayListClass)) {
792 ALOGE("getActiveMicrophones not an arraylist");
793 return (jint)AUDIO_JAVA_BAD_VALUE;
794 }
795
796 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
797 if (lpRecorder == NULL) {
798 jniThrowException(env, "java/lang/IllegalStateException",
799 "Unable to retrieve AudioRecord pointer for getActiveMicrophones()");
800 return (jint)AUDIO_JAVA_ERROR;
801 }
802
803 jint jStatus = AUDIO_JAVA_SUCCESS;
804 std::vector<media::MicrophoneInfo> activeMicrophones;
805 status_t status = lpRecorder->getActiveMicrophones(&activeMicrophones);
806 if (status != NO_ERROR) {
807 ALOGE_IF(status != NO_ERROR, "AudioRecord::getActiveMicrophones error %d", status);
808 jStatus = nativeToJavaStatus(status);
809 return jStatus;
810 }
811
812 for (size_t i = 0; i < activeMicrophones.size(); i++) {
813 jobject jMicrophoneInfo;
814 jStatus = convertMicrophoneInfoFromNative(env, &jMicrophoneInfo, &activeMicrophones[i]);
815 if (jStatus != AUDIO_JAVA_SUCCESS) {
816 return jStatus;
817 }
818 env->CallBooleanMethod(jActiveMicrophones, gArrayListMethods.add, jMicrophoneInfo);
819 env->DeleteLocalRef(jMicrophoneInfo);
820 }
821 return jStatus;
822}
823
Paul McLean6e7a47932019-03-15 10:43:41 -0700824static int android_media_AudioRecord_set_preferred_microphone_direction(
825 JNIEnv *env, jobject thiz, jint direction) {
Paul McLean525b4ca2018-11-06 16:37:19 -0700826 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
827 if (lpRecorder == NULL) {
828 jniThrowException(env, "java/lang/IllegalStateException",
Paul McLean6e7a47932019-03-15 10:43:41 -0700829 "Unable to retrieve AudioRecord pointer for setPreferredMicrophoneDirection()");
Paul McLean525b4ca2018-11-06 16:37:19 -0700830 return (jint)AUDIO_JAVA_ERROR;
831 }
832
833 jint jStatus = AUDIO_JAVA_SUCCESS;
Paul McLean6e7a47932019-03-15 10:43:41 -0700834 status_t status = lpRecorder->setPreferredMicrophoneDirection(
835 static_cast<audio_microphone_direction_t>(direction));
Paul McLean525b4ca2018-11-06 16:37:19 -0700836 if (status != NO_ERROR) {
837 jStatus = nativeToJavaStatus(status);
838 }
839
840 return jStatus;
841}
842
Paul McLean6e7a47932019-03-15 10:43:41 -0700843static int android_media_AudioRecord_set_preferred_microphone_field_dimension(
844 JNIEnv *env, jobject thiz, jfloat zoom) {
Paul McLean525b4ca2018-11-06 16:37:19 -0700845 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
846 if (lpRecorder == NULL) {
847 jniThrowException(env, "java/lang/IllegalStateException",
Paul McLean6e7a47932019-03-15 10:43:41 -0700848 "Unable to retrieve AudioRecord pointer for setPreferredMicrophoneFieldDimension()");
Paul McLean525b4ca2018-11-06 16:37:19 -0700849 return (jint)AUDIO_JAVA_ERROR;
850 }
851
852 jint jStatus = AUDIO_JAVA_SUCCESS;
Paul McLean6e7a47932019-03-15 10:43:41 -0700853 status_t status = lpRecorder->setPreferredMicrophoneFieldDimension(zoom);
Paul McLean525b4ca2018-11-06 16:37:19 -0700854 if (status != NO_ERROR) {
855 jStatus = nativeToJavaStatus(status);
856 }
857
858 return jStatus;
859}
860
jiabin23675f62018-01-17 18:05:25 -0800861// ----------------------------------------------------------------------------
Eric Laurent2dc54832018-11-20 17:49:43 -0800862static jint android_media_AudioRecord_get_port_id(JNIEnv *env, jobject thiz) {
863 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
864 if (lpRecorder == NULL) {
865 jniThrowException(env, "java/lang/IllegalStateException",
866 "Unable to retrieve AudioRecord pointer for getId()");
867 return (jint)AUDIO_PORT_HANDLE_NONE;
868 }
869 return (jint)lpRecorder->getPortId();
870}
871
872
873// ----------------------------------------------------------------------------
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800874// ----------------------------------------------------------------------------
Daniel Micay76f6a862015-09-19 17:31:01 -0400875static const JNINativeMethod gMethods[] = {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800876 // name, signature, funcPtr
Eric Laurent505e5c82012-03-29 15:19:36 -0700877 {"native_start", "(II)I", (void *)android_media_AudioRecord_start},
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800878 {"native_stop", "()V", (void *)android_media_AudioRecord_stop},
Paul McLean9b09e532016-01-26 14:43:35 -0700879 {"native_setup", "(Ljava/lang/Object;Ljava/lang/Object;[IIIII[ILjava/lang/String;J)I",
880 (void *)android_media_AudioRecord_setup},
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800881 {"native_finalize", "()V", (void *)android_media_AudioRecord_finalize},
882 {"native_release", "()V", (void *)android_media_AudioRecord_release},
Glenn Kasten18db49a2012-03-12 16:29:55 -0700883 {"native_read_in_byte_array",
Andy Hung0e9e2f32015-04-13 23:47:18 -0700884 "([BIIZ)I",
885 (void *)android_media_AudioRecord_readInArray<jbyteArray>},
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800886 {"native_read_in_short_array",
Andy Hung0e9e2f32015-04-13 23:47:18 -0700887 "([SIIZ)I",
888 (void *)android_media_AudioRecord_readInArray<jshortArray>},
Andy Hung58b0f3f2015-03-27 17:59:45 -0700889 {"native_read_in_float_array",
Andy Hung0e9e2f32015-04-13 23:47:18 -0700890 "([FIIZ)I",
891 (void *)android_media_AudioRecord_readInArray<jfloatArray>},
892 {"native_read_in_direct_buffer","(Ljava/lang/Object;IZ)I",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800893 (void *)android_media_AudioRecord_readInDirectBuffer},
Andy Hunge90a0a82015-05-19 15:44:31 -0700894 {"native_get_buffer_size_in_frames",
895 "()I", (void *)android_media_AudioRecord_get_buffer_size_in_frames},
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800896 {"native_set_marker_pos","(I)I", (void *)android_media_AudioRecord_set_marker_pos},
897 {"native_get_marker_pos","()I", (void *)android_media_AudioRecord_get_marker_pos},
898 {"native_set_pos_update_period",
899 "(I)I", (void *)android_media_AudioRecord_set_pos_update_period},
900 {"native_get_pos_update_period",
901 "()I", (void *)android_media_AudioRecord_get_pos_update_period},
902 {"native_get_min_buff_size",
903 "(III)I", (void *)android_media_AudioRecord_get_min_buff_size},
Ray Essick510225b2018-01-24 14:27:16 -0800904 {"native_getMetrics", "()Landroid/os/PersistableBundle;",
905 (void *)android_media_AudioRecord_native_getMetrics},
Paul McLean2d6de4c2015-04-17 13:13:28 -0600906 {"native_setInputDevice", "(I)Z", (void *)android_media_AudioRecord_setInputDevice},
Eric Laurent4bcdba82015-05-01 11:37:49 -0700907 {"native_getRoutedDeviceId", "()I", (void *)android_media_AudioRecord_getRoutedDeviceId},
908 {"native_enableDeviceCallback", "()V", (void *)android_media_AudioRecord_enableDeviceCallback},
909 {"native_disableDeviceCallback", "()V",
910 (void *)android_media_AudioRecord_disableDeviceCallback},
Andy Hung0ad99c02016-01-15 17:53:47 -0800911 {"native_get_timestamp", "(Landroid/media/AudioTimestamp;I)I",
912 (void *)android_media_AudioRecord_get_timestamp},
jiabin23675f62018-01-17 18:05:25 -0800913 {"native_get_active_microphones", "(Ljava/util/ArrayList;)I",
914 (void *)android_media_AudioRecord_get_active_microphones},
Eric Laurent2dc54832018-11-20 17:49:43 -0800915 {"native_getPortId", "()I", (void *)android_media_AudioRecord_get_port_id},
Paul McLean6e7a47932019-03-15 10:43:41 -0700916 {"native_set_preferred_microphone_direction", "(I)I",
917 (void *)android_media_AudioRecord_set_preferred_microphone_direction},
918 {"native_set_preferred_microphone_field_dimension", "(F)I",
919 (void *)android_media_AudioRecord_set_preferred_microphone_field_dimension},
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800920};
921
922// field names found in android/media/AudioRecord.java
923#define JAVA_POSTEVENT_CALLBACK_NAME "postEventFromNative"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800924#define JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME "mNativeRecorderInJavaObj"
925#define JAVA_NATIVECALLBACKINFO_FIELD_NAME "mNativeCallbackCookie"
Eric Laurent4bcdba82015-05-01 11:37:49 -0700926#define JAVA_NATIVEDEVICECALLBACK_FIELD_NAME "mNativeDeviceCallback"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800927
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800928// ----------------------------------------------------------------------------
929int register_android_media_AudioRecord(JNIEnv *env)
930{
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800931 javaAudioRecordFields.postNativeEventInJava = NULL;
932 javaAudioRecordFields.nativeRecorderInJavaObj = NULL;
933 javaAudioRecordFields.nativeCallbackCookie = NULL;
Eric Laurent4bcdba82015-05-01 11:37:49 -0700934 javaAudioRecordFields.nativeDeviceCallback = NULL;
Glenn Kasten18db49a2012-03-12 16:29:55 -0700935
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800936
937 // Get the AudioRecord class
Andreas Gampeed6b9df2014-11-20 22:02:20 -0800938 jclass audioRecordClass = FindClassOrDie(env, kClassPathName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800939 // Get the postEvent method
Andreas Gampeed6b9df2014-11-20 22:02:20 -0800940 javaAudioRecordFields.postNativeEventInJava = GetStaticMethodIDOrDie(env,
941 audioRecordClass, JAVA_POSTEVENT_CALLBACK_NAME,
942 "(Ljava/lang/Object;IIILjava/lang/Object;)V");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800943
944 // Get the variables
945 // mNativeRecorderInJavaObj
Andreas Gampeed6b9df2014-11-20 22:02:20 -0800946 javaAudioRecordFields.nativeRecorderInJavaObj = GetFieldIDOrDie(env,
947 audioRecordClass, JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME, "J");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800948 // mNativeCallbackCookie
Andreas Gampeed6b9df2014-11-20 22:02:20 -0800949 javaAudioRecordFields.nativeCallbackCookie = GetFieldIDOrDie(env,
950 audioRecordClass, JAVA_NATIVECALLBACKINFO_FIELD_NAME, "J");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800951
Eric Laurent4bcdba82015-05-01 11:37:49 -0700952 javaAudioRecordFields.nativeDeviceCallback = GetFieldIDOrDie(env,
953 audioRecordClass, JAVA_NATIVEDEVICECALLBACK_FIELD_NAME, "J");
954
Andy Hung0ad99c02016-01-15 17:53:47 -0800955 // Get the RecordTimestamp class and fields
956 jclass audioTimestampClass = FindClassOrDie(env, "android/media/AudioTimestamp");
957 javaAudioTimestampFields.fieldFramePosition =
958 GetFieldIDOrDie(env, audioTimestampClass, "framePosition", "J");
959 javaAudioTimestampFields.fieldNanoTime =
960 GetFieldIDOrDie(env, audioTimestampClass, "nanoTime", "J");
961
jiabin23675f62018-01-17 18:05:25 -0800962 jclass arrayListClass = FindClassOrDie(env, "java/util/ArrayList");
963 gArrayListClass = MakeGlobalRefOrDie(env, arrayListClass);
964 gArrayListMethods.add = GetMethodIDOrDie(env, arrayListClass, "add", "(Ljava/lang/Object;)Z");
965
Andreas Gampeed6b9df2014-11-20 22:02:20 -0800966 return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800967}
968
969// ----------------------------------------------------------------------------