1 /*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 //#define LOG_NDEBUG 0
18
19 #define LOG_TAG "AudioRecord-JNI"
20
21 #include <inttypes.h>
22 #include <jni.h>
23 #include <nativehelper/JNIHelp.h>
24 #include "core_jni_helpers.h"
25
26 #include <utils/Log.h>
27 #include <media/AudioRecord.h>
28
29 #include <nativehelper/ScopedUtfChars.h>
30
31 #include "android_media_AudioFormat.h"
32 #include "android_media_AudioErrors.h"
33 #include "android_media_DeviceCallback.h"
34
35 // ----------------------------------------------------------------------------
36
37 using namespace android;
38
39 // ----------------------------------------------------------------------------
40 static const char* const kClassPathName = "android/media/AudioRecord";
41 static const char* const kAudioAttributesClassPathName = "android/media/AudioAttributes";
42
43 struct audio_record_fields_t {
44 // these fields provide access from C++ to the...
45 jmethodID postNativeEventInJava; //... event post callback method
46 jfieldID nativeRecorderInJavaObj; // provides access to the C++ AudioRecord object
47 jfieldID nativeCallbackCookie; // provides access to the AudioRecord callback data
48 jfieldID nativeDeviceCallback; // provides access to the JNIDeviceCallback instance
49 };
50 struct audio_attributes_fields_t {
51 jfieldID fieldRecSource; // AudioAttributes.mSource
52 jfieldID fieldFlags; // AudioAttributes.mFlags
53 jfieldID fieldFormattedTags;// AudioAttributes.mFormattedTags
54 };
55 static audio_attributes_fields_t javaAudioAttrFields;
56 static audio_record_fields_t javaAudioRecordFields;
57 static struct {
58 jfieldID fieldFramePosition; // AudioTimestamp.framePosition
59 jfieldID fieldNanoTime; // AudioTimestamp.nanoTime
60 } javaAudioTimestampFields;
61
62 struct audiorecord_callback_cookie {
63 jclass audioRecord_class;
64 jobject audioRecord_ref;
65 bool busy;
66 Condition cond;
67 };
68
69 static Mutex sLock;
70 static SortedVector <audiorecord_callback_cookie *> sAudioRecordCallBackCookies;
71
72 // ----------------------------------------------------------------------------
73
74 #define AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT (-16)
75 #define AUDIORECORD_ERROR_SETUP_INVALIDCHANNELMASK (-17)
76 #define AUDIORECORD_ERROR_SETUP_INVALIDFORMAT (-18)
77 #define AUDIORECORD_ERROR_SETUP_INVALIDSOURCE (-19)
78 #define AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED (-20)
79
80 // ----------------------------------------------------------------------------
recorderCallback(int event,void * user,void * info)81 static void recorderCallback(int event, void* user, void *info) {
82
83 audiorecord_callback_cookie *callbackInfo = (audiorecord_callback_cookie *)user;
84 {
85 Mutex::Autolock l(sLock);
86 if (sAudioRecordCallBackCookies.indexOf(callbackInfo) < 0) {
87 return;
88 }
89 callbackInfo->busy = true;
90 }
91
92 switch (event) {
93 case AudioRecord::EVENT_MARKER: {
94 JNIEnv *env = AndroidRuntime::getJNIEnv();
95 if (user != NULL && env != NULL) {
96 env->CallStaticVoidMethod(
97 callbackInfo->audioRecord_class,
98 javaAudioRecordFields.postNativeEventInJava,
99 callbackInfo->audioRecord_ref, event, 0,0, NULL);
100 if (env->ExceptionCheck()) {
101 env->ExceptionDescribe();
102 env->ExceptionClear();
103 }
104 }
105 } break;
106
107 case AudioRecord::EVENT_NEW_POS: {
108 JNIEnv *env = AndroidRuntime::getJNIEnv();
109 if (user != NULL && env != NULL) {
110 env->CallStaticVoidMethod(
111 callbackInfo->audioRecord_class,
112 javaAudioRecordFields.postNativeEventInJava,
113 callbackInfo->audioRecord_ref, event, 0,0, NULL);
114 if (env->ExceptionCheck()) {
115 env->ExceptionDescribe();
116 env->ExceptionClear();
117 }
118 }
119 } break;
120 }
121
122 {
123 Mutex::Autolock l(sLock);
124 callbackInfo->busy = false;
125 callbackInfo->cond.broadcast();
126 }
127 }
128
getJniDeviceCallback(JNIEnv * env,jobject thiz)129 static sp<JNIDeviceCallback> getJniDeviceCallback(JNIEnv* env, jobject thiz)
130 {
131 Mutex::Autolock l(sLock);
132 JNIDeviceCallback* const cb =
133 (JNIDeviceCallback*)env->GetLongField(thiz,
134 javaAudioRecordFields.nativeDeviceCallback);
135 return sp<JNIDeviceCallback>(cb);
136 }
137
setJniDeviceCallback(JNIEnv * env,jobject thiz,const sp<JNIDeviceCallback> & cb)138 static sp<JNIDeviceCallback> setJniDeviceCallback(JNIEnv* env,
139 jobject thiz,
140 const sp<JNIDeviceCallback>& cb)
141 {
142 Mutex::Autolock l(sLock);
143 sp<JNIDeviceCallback> old =
144 (JNIDeviceCallback*)env->GetLongField(thiz,
145 javaAudioRecordFields.nativeDeviceCallback);
146 if (cb.get()) {
147 cb->incStrong((void*)setJniDeviceCallback);
148 }
149 if (old != 0) {
150 old->decStrong((void*)setJniDeviceCallback);
151 }
152 env->SetLongField(thiz, javaAudioRecordFields.nativeDeviceCallback, (jlong)cb.get());
153 return old;
154 }
155
156 // ----------------------------------------------------------------------------
getAudioRecord(JNIEnv * env,jobject thiz)157 static sp<AudioRecord> getAudioRecord(JNIEnv* env, jobject thiz)
158 {
159 Mutex::Autolock l(sLock);
160 AudioRecord* const ar =
161 (AudioRecord*)env->GetLongField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
162 return sp<AudioRecord>(ar);
163 }
164
setAudioRecord(JNIEnv * env,jobject thiz,const sp<AudioRecord> & ar)165 static sp<AudioRecord> setAudioRecord(JNIEnv* env, jobject thiz, const sp<AudioRecord>& ar)
166 {
167 Mutex::Autolock l(sLock);
168 sp<AudioRecord> old =
169 (AudioRecord*)env->GetLongField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
170 if (ar.get()) {
171 ar->incStrong((void*)setAudioRecord);
172 }
173 if (old != 0) {
174 old->decStrong((void*)setAudioRecord);
175 }
176 env->SetLongField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj, (jlong)ar.get());
177 return old;
178 }
179
180 // ----------------------------------------------------------------------------
181 static jint
android_media_AudioRecord_setup(JNIEnv * env,jobject thiz,jobject weak_this,jobject jaa,jintArray jSampleRate,jint channelMask,jint channelIndexMask,jint audioFormat,jint buffSizeInBytes,jintArray jSession,jstring opPackageName,jlong nativeRecordInJavaObj)182 android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject weak_this,
183 jobject jaa, jintArray jSampleRate, jint channelMask, jint channelIndexMask,
184 jint audioFormat, jint buffSizeInBytes, jintArray jSession, jstring opPackageName,
185 jlong nativeRecordInJavaObj)
186 {
187 //ALOGV(">> Entering android_media_AudioRecord_setup");
188 //ALOGV("sampleRate=%d, audioFormat=%d, channel mask=%x, buffSizeInBytes=%d "
189 // "nativeRecordInJavaObj=0x%llX",
190 // sampleRateInHertz, audioFormat, channelMask, buffSizeInBytes, nativeRecordInJavaObj);
191 audio_channel_mask_t localChanMask = inChannelMaskToNative(channelMask);
192
193 if (jSession == NULL) {
194 ALOGE("Error creating AudioRecord: invalid session ID pointer");
195 return (jint) AUDIO_JAVA_ERROR;
196 }
197
198 jint* nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
199 if (nSession == NULL) {
200 ALOGE("Error creating AudioRecord: Error retrieving session id pointer");
201 return (jint) AUDIO_JAVA_ERROR;
202 }
203 audio_session_t sessionId = (audio_session_t) nSession[0];
204 env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
205 nSession = NULL;
206
207 audio_attributes_t *paa = NULL;
208 sp<AudioRecord> lpRecorder = 0;
209 audiorecord_callback_cookie *lpCallbackData = NULL;
210
211 jclass clazz = env->GetObjectClass(thiz);
212 if (clazz == NULL) {
213 ALOGE("Can't find %s when setting up callback.", kClassPathName);
214 return (jint) AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED;
215 }
216
217 // if we pass in an existing *Native* AudioRecord, we don't need to create/initialize one.
218 if (nativeRecordInJavaObj == 0) {
219 if (jaa == 0) {
220 ALOGE("Error creating AudioRecord: invalid audio attributes");
221 return (jint) AUDIO_JAVA_ERROR;
222 }
223
224 if (jSampleRate == 0) {
225 ALOGE("Error creating AudioRecord: invalid sample rates");
226 return (jint) AUDIO_JAVA_ERROR;
227 }
228 jint elements[1];
229 env->GetIntArrayRegion(jSampleRate, 0, 1, elements);
230 int sampleRateInHertz = elements[0];
231
232 // channel index mask takes priority over channel position masks.
233 if (channelIndexMask) {
234 // Java channel index masks need the representation bits set.
235 localChanMask = audio_channel_mask_from_representation_and_bits(
236 AUDIO_CHANNEL_REPRESENTATION_INDEX,
237 channelIndexMask);
238 }
239 // Java channel position masks map directly to the native definition
240
241 if (!audio_is_input_channel(localChanMask)) {
242 ALOGE("Error creating AudioRecord: channel mask %#x is not valid.", localChanMask);
243 return (jint) AUDIORECORD_ERROR_SETUP_INVALIDCHANNELMASK;
244 }
245 uint32_t channelCount = audio_channel_count_from_in_mask(localChanMask);
246
247 // compare the format against the Java constants
248 audio_format_t format = audioFormatToNative(audioFormat);
249 if (format == AUDIO_FORMAT_INVALID) {
250 ALOGE("Error creating AudioRecord: unsupported audio format %d.", audioFormat);
251 return (jint) AUDIORECORD_ERROR_SETUP_INVALIDFORMAT;
252 }
253
254 size_t bytesPerSample = audio_bytes_per_sample(format);
255
256 if (buffSizeInBytes == 0) {
257 ALOGE("Error creating AudioRecord: frameCount is 0.");
258 return (jint) AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT;
259 }
260 size_t frameSize = channelCount * bytesPerSample;
261 size_t frameCount = buffSizeInBytes / frameSize;
262
263 ScopedUtfChars opPackageNameStr(env, opPackageName);
264
265 // create an uninitialized AudioRecord object
266 lpRecorder = new AudioRecord(String16(opPackageNameStr.c_str()));
267
268 // read the AudioAttributes values
269 paa = (audio_attributes_t *) calloc(1, sizeof(audio_attributes_t));
270 const jstring jtags =
271 (jstring) env->GetObjectField(jaa, javaAudioAttrFields.fieldFormattedTags);
272 const char* tags = env->GetStringUTFChars(jtags, NULL);
273 // copying array size -1, char array for tags was calloc'd, no need to NULL-terminate it
274 strncpy(paa->tags, tags, AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1);
275 env->ReleaseStringUTFChars(jtags, tags);
276 paa->source = (audio_source_t) env->GetIntField(jaa, javaAudioAttrFields.fieldRecSource);
277 paa->flags = (audio_flags_mask_t)env->GetIntField(jaa, javaAudioAttrFields.fieldFlags);
278 ALOGV("AudioRecord_setup for source=%d tags=%s flags=%08x", paa->source, paa->tags, paa->flags);
279
280 audio_input_flags_t flags = AUDIO_INPUT_FLAG_NONE;
281 if (paa->flags & AUDIO_FLAG_HW_HOTWORD) {
282 flags = AUDIO_INPUT_FLAG_HW_HOTWORD;
283 }
284 // create the callback information:
285 // this data will be passed with every AudioRecord callback
286 lpCallbackData = new audiorecord_callback_cookie;
287 lpCallbackData->audioRecord_class = (jclass)env->NewGlobalRef(clazz);
288 // we use a weak reference so the AudioRecord object can be garbage collected.
289 lpCallbackData->audioRecord_ref = env->NewGlobalRef(weak_this);
290 lpCallbackData->busy = false;
291
292 const status_t status = lpRecorder->set(paa->source,
293 sampleRateInHertz,
294 format, // word length, PCM
295 localChanMask,
296 frameCount,
297 recorderCallback,// callback_t
298 lpCallbackData,// void* user
299 0, // notificationFrames,
300 true, // threadCanCallJava
301 sessionId,
302 AudioRecord::TRANSFER_DEFAULT,
303 flags,
304 -1, -1, // default uid, pid
305 paa);
306
307 if (status != NO_ERROR) {
308 ALOGE("Error creating AudioRecord instance: initialization check failed with status %d.",
309 status);
310 goto native_init_failure;
311 }
312 } else { // end if nativeRecordInJavaObj == 0)
313 lpRecorder = (AudioRecord*)nativeRecordInJavaObj;
314 // TODO: We need to find out which members of the Java AudioRecord might need to be
315 // initialized from the Native AudioRecord
316 // these are directly returned from getters:
317 // mSampleRate
318 // mRecordSource
319 // mAudioFormat
320 // mChannelMask
321 // mChannelCount
322 // mState (?)
323 // mRecordingState (?)
324 // mPreferredDevice
325
326 // create the callback information:
327 // this data will be passed with every AudioRecord callback
328 lpCallbackData = new audiorecord_callback_cookie;
329 lpCallbackData->audioRecord_class = (jclass)env->NewGlobalRef(clazz);
330 // we use a weak reference so the AudioRecord object can be garbage collected.
331 lpCallbackData->audioRecord_ref = env->NewGlobalRef(weak_this);
332 lpCallbackData->busy = false;
333 }
334
335 nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
336 if (nSession == NULL) {
337 ALOGE("Error creating AudioRecord: Error retrieving session id pointer");
338 goto native_init_failure;
339 }
340 // read the audio session ID back from AudioRecord in case a new session was created during set()
341 nSession[0] = lpRecorder->getSessionId();
342 env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
343 nSession = NULL;
344
345 {
346 const jint elements[1] = { (jint) lpRecorder->getSampleRate() };
347 env->SetIntArrayRegion(jSampleRate, 0, 1, elements);
348 }
349
350 { // scope for the lock
351 Mutex::Autolock l(sLock);
352 sAudioRecordCallBackCookies.add(lpCallbackData);
353 }
354 // save our newly created C++ AudioRecord in the "nativeRecorderInJavaObj" field
355 // of the Java object
356 setAudioRecord(env, thiz, lpRecorder);
357
358 // save our newly created callback information in the "nativeCallbackCookie" field
359 // of the Java object (in mNativeCallbackCookie) so we can free the memory in finalize()
360 env->SetLongField(thiz, javaAudioRecordFields.nativeCallbackCookie, (jlong)lpCallbackData);
361
362 return (jint) AUDIO_JAVA_SUCCESS;
363
364 // failure:
365 native_init_failure:
366 env->DeleteGlobalRef(lpCallbackData->audioRecord_class);
367 env->DeleteGlobalRef(lpCallbackData->audioRecord_ref);
368 delete lpCallbackData;
369 env->SetLongField(thiz, javaAudioRecordFields.nativeCallbackCookie, 0);
370
371 // lpRecorder goes out of scope, so reference count drops to zero
372 return (jint) AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED;
373 }
374
375
376
377 // ----------------------------------------------------------------------------
378 static jint
android_media_AudioRecord_start(JNIEnv * env,jobject thiz,jint event,jint triggerSession)379 android_media_AudioRecord_start(JNIEnv *env, jobject thiz, jint event, jint triggerSession)
380 {
381 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
382 if (lpRecorder == NULL ) {
383 jniThrowException(env, "java/lang/IllegalStateException", NULL);
384 return (jint) AUDIO_JAVA_ERROR;
385 }
386
387 return nativeToJavaStatus(
388 lpRecorder->start((AudioSystem::sync_event_t)event, (audio_session_t) triggerSession));
389 }
390
391
392 // ----------------------------------------------------------------------------
393 static void
android_media_AudioRecord_stop(JNIEnv * env,jobject thiz)394 android_media_AudioRecord_stop(JNIEnv *env, jobject thiz)
395 {
396 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
397 if (lpRecorder == NULL ) {
398 jniThrowException(env, "java/lang/IllegalStateException", NULL);
399 return;
400 }
401
402 lpRecorder->stop();
403 //ALOGV("Called lpRecorder->stop()");
404 }
405
406
407 // ----------------------------------------------------------------------------
408
409 #define CALLBACK_COND_WAIT_TIMEOUT_MS 1000
android_media_AudioRecord_release(JNIEnv * env,jobject thiz)410 static void android_media_AudioRecord_release(JNIEnv *env, jobject thiz) {
411 sp<AudioRecord> lpRecorder = setAudioRecord(env, thiz, 0);
412 if (lpRecorder == NULL) {
413 return;
414 }
415 ALOGV("About to delete lpRecorder: %p", lpRecorder.get());
416 lpRecorder->stop();
417
418 audiorecord_callback_cookie *lpCookie = (audiorecord_callback_cookie *)env->GetLongField(
419 thiz, javaAudioRecordFields.nativeCallbackCookie);
420
421 // reset the native resources in the Java object so any attempt to access
422 // them after a call to release fails.
423 env->SetLongField(thiz, javaAudioRecordFields.nativeCallbackCookie, 0);
424
425 // delete the callback information
426 if (lpCookie) {
427 Mutex::Autolock l(sLock);
428 ALOGV("deleting lpCookie: %p", lpCookie);
429 while (lpCookie->busy) {
430 if (lpCookie->cond.waitRelative(sLock,
431 milliseconds(CALLBACK_COND_WAIT_TIMEOUT_MS)) !=
432 NO_ERROR) {
433 break;
434 }
435 }
436 sAudioRecordCallBackCookies.remove(lpCookie);
437 env->DeleteGlobalRef(lpCookie->audioRecord_class);
438 env->DeleteGlobalRef(lpCookie->audioRecord_ref);
439 delete lpCookie;
440 }
441 }
442
443
444 // ----------------------------------------------------------------------------
android_media_AudioRecord_finalize(JNIEnv * env,jobject thiz)445 static void android_media_AudioRecord_finalize(JNIEnv *env, jobject thiz) {
446 android_media_AudioRecord_release(env, thiz);
447 }
448
449 // overloaded JNI array helper functions
450 static inline
envGetArrayElements(JNIEnv * env,jbyteArray array,jboolean * isCopy)451 jbyte *envGetArrayElements(JNIEnv *env, jbyteArray array, jboolean *isCopy) {
452 return env->GetByteArrayElements(array, isCopy);
453 }
454
455 static inline
envReleaseArrayElements(JNIEnv * env,jbyteArray array,jbyte * elems,jint mode)456 void envReleaseArrayElements(JNIEnv *env, jbyteArray array, jbyte *elems, jint mode) {
457 env->ReleaseByteArrayElements(array, elems, mode);
458 }
459
460 static inline
envGetArrayElements(JNIEnv * env,jshortArray array,jboolean * isCopy)461 jshort *envGetArrayElements(JNIEnv *env, jshortArray array, jboolean *isCopy) {
462 return env->GetShortArrayElements(array, isCopy);
463 }
464
465 static inline
envReleaseArrayElements(JNIEnv * env,jshortArray array,jshort * elems,jint mode)466 void envReleaseArrayElements(JNIEnv *env, jshortArray array, jshort *elems, jint mode) {
467 env->ReleaseShortArrayElements(array, elems, mode);
468 }
469
470 static inline
envGetArrayElements(JNIEnv * env,jfloatArray array,jboolean * isCopy)471 jfloat *envGetArrayElements(JNIEnv *env, jfloatArray array, jboolean *isCopy) {
472 return env->GetFloatArrayElements(array, isCopy);
473 }
474
475 static inline
envReleaseArrayElements(JNIEnv * env,jfloatArray array,jfloat * elems,jint mode)476 void envReleaseArrayElements(JNIEnv *env, jfloatArray array, jfloat *elems, jint mode) {
477 env->ReleaseFloatArrayElements(array, elems, mode);
478 }
479
480 static inline
interpretReadSizeError(ssize_t readSize)481 jint interpretReadSizeError(ssize_t readSize) {
482 if (readSize == WOULD_BLOCK) {
483 return (jint)0;
484 } else if (readSize == NO_INIT) {
485 return AUDIO_JAVA_DEAD_OBJECT;
486 } else {
487 ALOGE("Error %zd during AudioRecord native read", readSize);
488 return nativeToJavaStatus(readSize);
489 }
490 }
491
492 template <typename T>
android_media_AudioRecord_readInArray(JNIEnv * env,jobject thiz,T javaAudioData,jint offsetInSamples,jint sizeInSamples,jboolean isReadBlocking)493 static jint android_media_AudioRecord_readInArray(JNIEnv *env, jobject thiz,
494 T javaAudioData,
495 jint offsetInSamples, jint sizeInSamples,
496 jboolean isReadBlocking) {
497 // get the audio recorder from which we'll read new audio samples
498 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
499 if (lpRecorder == NULL) {
500 ALOGE("Unable to retrieve AudioRecord object");
501 return (jint)AUDIO_JAVA_INVALID_OPERATION;
502 }
503
504 if (javaAudioData == NULL) {
505 ALOGE("Invalid Java array to store recorded audio");
506 return (jint)AUDIO_JAVA_BAD_VALUE;
507 }
508
509 // NOTE: We may use GetPrimitiveArrayCritical() when the JNI implementation changes in such
510 // a way that it becomes much more efficient. When doing so, we will have to prevent the
511 // AudioSystem callback to be called while in critical section (in case of media server
512 // process crash for instance)
513
514 // get the pointer to where we'll record the audio
515 auto *recordBuff = envGetArrayElements(env, javaAudioData, NULL);
516 if (recordBuff == NULL) {
517 ALOGE("Error retrieving destination for recorded audio data");
518 return (jint)AUDIO_JAVA_BAD_VALUE;
519 }
520
521 // read the new audio data from the native AudioRecord object
522 const size_t sizeInBytes = sizeInSamples * sizeof(*recordBuff);
523 ssize_t readSize = lpRecorder->read(
524 recordBuff + offsetInSamples, sizeInBytes, isReadBlocking == JNI_TRUE /* blocking */);
525
526 envReleaseArrayElements(env, javaAudioData, recordBuff, 0);
527
528 if (readSize < 0) {
529 return interpretReadSizeError(readSize);
530 }
531 return (jint)(readSize / sizeof(*recordBuff));
532 }
533
534 // ----------------------------------------------------------------------------
android_media_AudioRecord_readInDirectBuffer(JNIEnv * env,jobject thiz,jobject jBuffer,jint sizeInBytes,jboolean isReadBlocking)535 static jint android_media_AudioRecord_readInDirectBuffer(JNIEnv *env, jobject thiz,
536 jobject jBuffer, jint sizeInBytes,
537 jboolean isReadBlocking) {
538 // get the audio recorder from which we'll read new audio samples
539 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
540 if (lpRecorder==NULL)
541 return (jint)AUDIO_JAVA_INVALID_OPERATION;
542
543 // direct buffer and direct access supported?
544 long capacity = env->GetDirectBufferCapacity(jBuffer);
545 if (capacity == -1) {
546 // buffer direct access is not supported
547 ALOGE("Buffer direct access is not supported, can't record");
548 return (jint)AUDIO_JAVA_BAD_VALUE;
549 }
550 //ALOGV("capacity = %ld", capacity);
551 jbyte* nativeFromJavaBuf = (jbyte*) env->GetDirectBufferAddress(jBuffer);
552 if (nativeFromJavaBuf==NULL) {
553 ALOGE("Buffer direct access is not supported, can't record");
554 return (jint)AUDIO_JAVA_BAD_VALUE;
555 }
556
557 // read new data from the recorder
558 ssize_t readSize = lpRecorder->read(nativeFromJavaBuf,
559 capacity < sizeInBytes ? capacity : sizeInBytes,
560 isReadBlocking == JNI_TRUE /* blocking */);
561 if (readSize < 0) {
562 return interpretReadSizeError(readSize);
563 }
564 return (jint)readSize;
565 }
566
567 // ----------------------------------------------------------------------------
android_media_AudioRecord_get_buffer_size_in_frames(JNIEnv * env,jobject thiz)568 static jint android_media_AudioRecord_get_buffer_size_in_frames(JNIEnv *env, jobject thiz) {
569 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
570 if (lpRecorder == NULL) {
571 jniThrowException(env, "java/lang/IllegalStateException",
572 "Unable to retrieve AudioRecord pointer for frameCount()");
573 return (jint)AUDIO_JAVA_ERROR;
574 }
575 return lpRecorder->frameCount();
576 }
577
578 // ----------------------------------------------------------------------------
android_media_AudioRecord_set_marker_pos(JNIEnv * env,jobject thiz,jint markerPos)579 static jint android_media_AudioRecord_set_marker_pos(JNIEnv *env, jobject thiz,
580 jint markerPos) {
581
582 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
583 if (lpRecorder == NULL) {
584 jniThrowException(env, "java/lang/IllegalStateException",
585 "Unable to retrieve AudioRecord pointer for setMarkerPosition()");
586 return (jint)AUDIO_JAVA_ERROR;
587 }
588 return nativeToJavaStatus( lpRecorder->setMarkerPosition(markerPos) );
589 }
590
591
592 // ----------------------------------------------------------------------------
android_media_AudioRecord_get_marker_pos(JNIEnv * env,jobject thiz)593 static jint android_media_AudioRecord_get_marker_pos(JNIEnv *env, jobject thiz) {
594
595 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
596 uint32_t markerPos = 0;
597
598 if (lpRecorder == NULL) {
599 jniThrowException(env, "java/lang/IllegalStateException",
600 "Unable to retrieve AudioRecord pointer for getMarkerPosition()");
601 return (jint)AUDIO_JAVA_ERROR;
602 }
603 lpRecorder->getMarkerPosition(&markerPos);
604 return (jint)markerPos;
605 }
606
607
608 // ----------------------------------------------------------------------------
android_media_AudioRecord_set_pos_update_period(JNIEnv * env,jobject thiz,jint period)609 static jint android_media_AudioRecord_set_pos_update_period(JNIEnv *env, jobject thiz,
610 jint period) {
611
612 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
613
614 if (lpRecorder == NULL) {
615 jniThrowException(env, "java/lang/IllegalStateException",
616 "Unable to retrieve AudioRecord pointer for setPositionUpdatePeriod()");
617 return (jint)AUDIO_JAVA_ERROR;
618 }
619 return nativeToJavaStatus( lpRecorder->setPositionUpdatePeriod(period) );
620 }
621
622
623 // ----------------------------------------------------------------------------
android_media_AudioRecord_get_pos_update_period(JNIEnv * env,jobject thiz)624 static jint android_media_AudioRecord_get_pos_update_period(JNIEnv *env, jobject thiz) {
625
626 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
627 uint32_t period = 0;
628
629 if (lpRecorder == NULL) {
630 jniThrowException(env, "java/lang/IllegalStateException",
631 "Unable to retrieve AudioRecord pointer for getPositionUpdatePeriod()");
632 return (jint)AUDIO_JAVA_ERROR;
633 }
634 lpRecorder->getPositionUpdatePeriod(&period);
635 return (jint)period;
636 }
637
638
639 // ----------------------------------------------------------------------------
640 // returns the minimum required size for the successful creation of an AudioRecord instance.
641 // returns 0 if the parameter combination is not supported.
642 // return -1 if there was an error querying the buffer size.
android_media_AudioRecord_get_min_buff_size(JNIEnv * env,jobject thiz,jint sampleRateInHertz,jint channelCount,jint audioFormat)643 static jint android_media_AudioRecord_get_min_buff_size(JNIEnv *env, jobject thiz,
644 jint sampleRateInHertz, jint channelCount, jint audioFormat) {
645
646 ALOGV(">> android_media_AudioRecord_get_min_buff_size(%d, %d, %d)",
647 sampleRateInHertz, channelCount, audioFormat);
648
649 size_t frameCount = 0;
650 audio_format_t format = audioFormatToNative(audioFormat);
651 status_t result = AudioRecord::getMinFrameCount(&frameCount,
652 sampleRateInHertz,
653 format,
654 audio_channel_in_mask_from_count(channelCount));
655
656 if (result == BAD_VALUE) {
657 return 0;
658 }
659 if (result != NO_ERROR) {
660 return -1;
661 }
662 return frameCount * channelCount * audio_bytes_per_sample(format);
663 }
664
android_media_AudioRecord_setInputDevice(JNIEnv * env,jobject thiz,jint device_id)665 static jboolean android_media_AudioRecord_setInputDevice(
666 JNIEnv *env, jobject thiz, jint device_id) {
667
668 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
669 if (lpRecorder == 0) {
670 return false;
671 }
672 return lpRecorder->setInputDevice(device_id) == NO_ERROR;
673 }
674
android_media_AudioRecord_getRoutedDeviceId(JNIEnv * env,jobject thiz)675 static jint android_media_AudioRecord_getRoutedDeviceId(
676 JNIEnv *env, jobject thiz) {
677
678 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
679 if (lpRecorder == 0) {
680 return 0;
681 }
682 return (jint)lpRecorder->getRoutedDeviceId();
683 }
684
android_media_AudioRecord_enableDeviceCallback(JNIEnv * env,jobject thiz)685 static void android_media_AudioRecord_enableDeviceCallback(
686 JNIEnv *env, jobject thiz) {
687
688 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
689 if (lpRecorder == 0) {
690 return;
691 }
692 sp<JNIDeviceCallback> cb = getJniDeviceCallback(env, thiz);
693 if (cb != 0) {
694 return;
695 }
696 audiorecord_callback_cookie *cookie =
697 (audiorecord_callback_cookie *)env->GetLongField(thiz,
698 javaAudioRecordFields.nativeCallbackCookie);
699 if (cookie == NULL) {
700 return;
701 }
702
703 cb = new JNIDeviceCallback(env, thiz, cookie->audioRecord_ref,
704 javaAudioRecordFields.postNativeEventInJava);
705 status_t status = lpRecorder->addAudioDeviceCallback(cb);
706 if (status == NO_ERROR) {
707 setJniDeviceCallback(env, thiz, cb);
708 }
709 }
710
android_media_AudioRecord_disableDeviceCallback(JNIEnv * env,jobject thiz)711 static void android_media_AudioRecord_disableDeviceCallback(
712 JNIEnv *env, jobject thiz) {
713
714 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
715 if (lpRecorder == 0) {
716 return;
717 }
718 sp<JNIDeviceCallback> cb = setJniDeviceCallback(env, thiz, 0);
719 if (cb != 0) {
720 lpRecorder->removeAudioDeviceCallback(cb);
721 }
722 }
723
724 // ----------------------------------------------------------------------------
android_media_AudioRecord_get_timestamp(JNIEnv * env,jobject thiz,jobject timestamp,jint timebase)725 static jint android_media_AudioRecord_get_timestamp(JNIEnv *env, jobject thiz,
726 jobject timestamp, jint timebase) {
727 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
728
729 if (lpRecorder == NULL) {
730 jniThrowException(env, "java/lang/IllegalStateException",
731 "Unable to retrieve AudioRecord pointer for getTimestamp()");
732 return (jint)AUDIO_JAVA_ERROR;
733 }
734
735 ExtendedTimestamp ts;
736 jint status = nativeToJavaStatus(lpRecorder->getTimestamp(&ts));
737
738 if (status == AUDIO_JAVA_SUCCESS) {
739 // set the data
740 int64_t position, time;
741
742 status = nativeToJavaStatus(ts.getBestTimestamp(&position, &time, timebase));
743 if (status == AUDIO_JAVA_SUCCESS) {
744 env->SetLongField(
745 timestamp, javaAudioTimestampFields.fieldFramePosition, position);
746 env->SetLongField(
747 timestamp, javaAudioTimestampFields.fieldNanoTime, time);
748 }
749 }
750 return status;
751 }
752
753 // ----------------------------------------------------------------------------
754 // ----------------------------------------------------------------------------
755 static const JNINativeMethod gMethods[] = {
756 // name, signature, funcPtr
757 {"native_start", "(II)I", (void *)android_media_AudioRecord_start},
758 {"native_stop", "()V", (void *)android_media_AudioRecord_stop},
759 {"native_setup", "(Ljava/lang/Object;Ljava/lang/Object;[IIIII[ILjava/lang/String;J)I",
760 (void *)android_media_AudioRecord_setup},
761 {"native_finalize", "()V", (void *)android_media_AudioRecord_finalize},
762 {"native_release", "()V", (void *)android_media_AudioRecord_release},
763 {"native_read_in_byte_array",
764 "([BIIZ)I",
765 (void *)android_media_AudioRecord_readInArray<jbyteArray>},
766 {"native_read_in_short_array",
767 "([SIIZ)I",
768 (void *)android_media_AudioRecord_readInArray<jshortArray>},
769 {"native_read_in_float_array",
770 "([FIIZ)I",
771 (void *)android_media_AudioRecord_readInArray<jfloatArray>},
772 {"native_read_in_direct_buffer","(Ljava/lang/Object;IZ)I",
773 (void *)android_media_AudioRecord_readInDirectBuffer},
774 {"native_get_buffer_size_in_frames",
775 "()I", (void *)android_media_AudioRecord_get_buffer_size_in_frames},
776 {"native_set_marker_pos","(I)I", (void *)android_media_AudioRecord_set_marker_pos},
777 {"native_get_marker_pos","()I", (void *)android_media_AudioRecord_get_marker_pos},
778 {"native_set_pos_update_period",
779 "(I)I", (void *)android_media_AudioRecord_set_pos_update_period},
780 {"native_get_pos_update_period",
781 "()I", (void *)android_media_AudioRecord_get_pos_update_period},
782 {"native_get_min_buff_size",
783 "(III)I", (void *)android_media_AudioRecord_get_min_buff_size},
784 {"native_setInputDevice", "(I)Z", (void *)android_media_AudioRecord_setInputDevice},
785 {"native_getRoutedDeviceId", "()I", (void *)android_media_AudioRecord_getRoutedDeviceId},
786 {"native_enableDeviceCallback", "()V", (void *)android_media_AudioRecord_enableDeviceCallback},
787 {"native_disableDeviceCallback", "()V",
788 (void *)android_media_AudioRecord_disableDeviceCallback},
789 {"native_get_timestamp", "(Landroid/media/AudioTimestamp;I)I",
790 (void *)android_media_AudioRecord_get_timestamp},
791 };
792
793 // field names found in android/media/AudioRecord.java
794 #define JAVA_POSTEVENT_CALLBACK_NAME "postEventFromNative"
795 #define JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME "mNativeRecorderInJavaObj"
796 #define JAVA_NATIVECALLBACKINFO_FIELD_NAME "mNativeCallbackCookie"
797 #define JAVA_NATIVEDEVICECALLBACK_FIELD_NAME "mNativeDeviceCallback"
798
799 // ----------------------------------------------------------------------------
register_android_media_AudioRecord(JNIEnv * env)800 int register_android_media_AudioRecord(JNIEnv *env)
801 {
802 javaAudioRecordFields.postNativeEventInJava = NULL;
803 javaAudioRecordFields.nativeRecorderInJavaObj = NULL;
804 javaAudioRecordFields.nativeCallbackCookie = NULL;
805 javaAudioRecordFields.nativeDeviceCallback = NULL;
806
807
808 // Get the AudioRecord class
809 jclass audioRecordClass = FindClassOrDie(env, kClassPathName);
810 // Get the postEvent method
811 javaAudioRecordFields.postNativeEventInJava = GetStaticMethodIDOrDie(env,
812 audioRecordClass, JAVA_POSTEVENT_CALLBACK_NAME,
813 "(Ljava/lang/Object;IIILjava/lang/Object;)V");
814
815 // Get the variables
816 // mNativeRecorderInJavaObj
817 javaAudioRecordFields.nativeRecorderInJavaObj = GetFieldIDOrDie(env,
818 audioRecordClass, JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME, "J");
819 // mNativeCallbackCookie
820 javaAudioRecordFields.nativeCallbackCookie = GetFieldIDOrDie(env,
821 audioRecordClass, JAVA_NATIVECALLBACKINFO_FIELD_NAME, "J");
822
823 javaAudioRecordFields.nativeDeviceCallback = GetFieldIDOrDie(env,
824 audioRecordClass, JAVA_NATIVEDEVICECALLBACK_FIELD_NAME, "J");
825
826 // Get the AudioAttributes class and fields
827 jclass audioAttrClass = FindClassOrDie(env, kAudioAttributesClassPathName);
828 javaAudioAttrFields.fieldRecSource = GetFieldIDOrDie(env, audioAttrClass, "mSource", "I");
829 javaAudioAttrFields.fieldFlags = GetFieldIDOrDie(env, audioAttrClass, "mFlags", "I");
830 javaAudioAttrFields.fieldFormattedTags = GetFieldIDOrDie(env,
831 audioAttrClass, "mFormattedTags", "Ljava/lang/String;");
832
833 // Get the RecordTimestamp class and fields
834 jclass audioTimestampClass = FindClassOrDie(env, "android/media/AudioTimestamp");
835 javaAudioTimestampFields.fieldFramePosition =
836 GetFieldIDOrDie(env, audioTimestampClass, "framePosition", "J");
837 javaAudioTimestampFields.fieldNanoTime =
838 GetFieldIDOrDie(env, audioTimestampClass, "nanoTime", "J");
839
840 return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
841 }
842
843 // ----------------------------------------------------------------------------
844