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