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 <JNIHelp.h> 24 #include <android_runtime/AndroidRuntime.h> 25 26 #include <utils/Log.h> 27 #include <media/AudioRecord.h> 28 29 #include "android_media_AudioFormat.h" 30 #include "android_media_AudioErrors.h" 31 32 // ---------------------------------------------------------------------------- 33 34 using namespace android; 35 36 // ---------------------------------------------------------------------------- 37 static const char* const kClassPathName = "android/media/AudioRecord"; 38 static const char* const kAudioAttributesClassPathName = "android/media/AudioAttributes"; 39 40 struct audio_record_fields_t { 41 // these fields provide access from C++ to the... 42 jmethodID postNativeEventInJava; //... event post callback method 43 jfieldID nativeRecorderInJavaObj; // provides access to the C++ AudioRecord object 44 jfieldID nativeCallbackCookie; // provides access to the AudioRecord callback data 45 }; 46 struct audio_attributes_fields_t { 47 jfieldID fieldRecSource; // AudioAttributes.mSource 48 jfieldID fieldFlags; // AudioAttributes.mFlags 49 jfieldID fieldFormattedTags;// AudioAttributes.mFormattedTags 50 }; 51 static audio_attributes_fields_t javaAudioAttrFields; 52 static audio_record_fields_t javaAudioRecordFields; 53 54 struct audiorecord_callback_cookie { 55 jclass audioRecord_class; 56 jobject audioRecord_ref; 57 bool busy; 58 Condition cond; 59 }; 60 61 static Mutex sLock; 62 static SortedVector <audiorecord_callback_cookie *> sAudioRecordCallBackCookies; 63 64 // ---------------------------------------------------------------------------- 65 66 #define AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT -16 67 #define AUDIORECORD_ERROR_SETUP_INVALIDCHANNELMASK -17 68 #define AUDIORECORD_ERROR_SETUP_INVALIDFORMAT -18 69 #define AUDIORECORD_ERROR_SETUP_INVALIDSOURCE -19 70 #define AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED -20 71 72 // ---------------------------------------------------------------------------- 73 static void recorderCallback(int event, void* user, void *info) { 74 75 audiorecord_callback_cookie *callbackInfo = (audiorecord_callback_cookie *)user; 76 { 77 Mutex::Autolock l(sLock); 78 if (sAudioRecordCallBackCookies.indexOf(callbackInfo) < 0) { 79 return; 80 } 81 callbackInfo->busy = true; 82 } 83 84 switch (event) { 85 case AudioRecord::EVENT_MARKER: { 86 JNIEnv *env = AndroidRuntime::getJNIEnv(); 87 if (user != NULL && env != NULL) { 88 env->CallStaticVoidMethod( 89 callbackInfo->audioRecord_class, 90 javaAudioRecordFields.postNativeEventInJava, 91 callbackInfo->audioRecord_ref, event, 0,0, NULL); 92 if (env->ExceptionCheck()) { 93 env->ExceptionDescribe(); 94 env->ExceptionClear(); 95 } 96 } 97 } break; 98 99 case AudioRecord::EVENT_NEW_POS: { 100 JNIEnv *env = AndroidRuntime::getJNIEnv(); 101 if (user != NULL && env != NULL) { 102 env->CallStaticVoidMethod( 103 callbackInfo->audioRecord_class, 104 javaAudioRecordFields.postNativeEventInJava, 105 callbackInfo->audioRecord_ref, event, 0,0, NULL); 106 if (env->ExceptionCheck()) { 107 env->ExceptionDescribe(); 108 env->ExceptionClear(); 109 } 110 } 111 } break; 112 } 113 114 { 115 Mutex::Autolock l(sLock); 116 callbackInfo->busy = false; 117 callbackInfo->cond.broadcast(); 118 } 119 } 120 121 // ---------------------------------------------------------------------------- 122 static sp<AudioRecord> getAudioRecord(JNIEnv* env, jobject thiz) 123 { 124 Mutex::Autolock l(sLock); 125 AudioRecord* const ar = 126 (AudioRecord*)env->GetLongField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj); 127 return sp<AudioRecord>(ar); 128 } 129 130 static sp<AudioRecord> setAudioRecord(JNIEnv* env, jobject thiz, const sp<AudioRecord>& ar) 131 { 132 Mutex::Autolock l(sLock); 133 sp<AudioRecord> old = 134 (AudioRecord*)env->GetLongField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj); 135 if (ar.get()) { 136 ar->incStrong((void*)setAudioRecord); 137 } 138 if (old != 0) { 139 old->decStrong((void*)setAudioRecord); 140 } 141 env->SetLongField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj, (jlong)ar.get()); 142 return old; 143 } 144 145 // ---------------------------------------------------------------------------- 146 static jint 147 android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject weak_this, 148 jobject jaa, jint sampleRateInHertz, jint channelMask, 149 // Java channel masks map directly to the native definition 150 jint audioFormat, jint buffSizeInBytes, jintArray jSession) 151 { 152 //ALOGV(">> Entering android_media_AudioRecord_setup"); 153 //ALOGV("sampleRate=%d, audioFormat=%d, channel mask=%x, buffSizeInBytes=%d", 154 // sampleRateInHertz, audioFormat, channelMask, buffSizeInBytes); 155 156 if (jaa == 0) { 157 ALOGE("Error creating AudioRecord: invalid audio attributes"); 158 return (jint) AUDIO_JAVA_ERROR; 159 } 160 161 if (!audio_is_input_channel(channelMask)) { 162 ALOGE("Error creating AudioRecord: channel mask %#x is not valid.", channelMask); 163 return (jint) AUDIORECORD_ERROR_SETUP_INVALIDCHANNELMASK; 164 } 165 uint32_t channelCount = audio_channel_count_from_in_mask(channelMask); 166 167 // compare the format against the Java constants 168 audio_format_t format = audioFormatToNative(audioFormat); 169 if (format == AUDIO_FORMAT_INVALID) { 170 ALOGE("Error creating AudioRecord: unsupported audio format %d.", audioFormat); 171 return (jint) AUDIORECORD_ERROR_SETUP_INVALIDFORMAT; 172 } 173 174 size_t bytesPerSample = audio_bytes_per_sample(format); 175 176 if (buffSizeInBytes == 0) { 177 ALOGE("Error creating AudioRecord: frameCount is 0."); 178 return (jint) AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT; 179 } 180 size_t frameSize = channelCount * bytesPerSample; 181 size_t frameCount = buffSizeInBytes / frameSize; 182 183 jclass clazz = env->GetObjectClass(thiz); 184 if (clazz == NULL) { 185 ALOGE("Can't find %s when setting up callback.", kClassPathName); 186 return (jint) AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED; 187 } 188 189 if (jSession == NULL) { 190 ALOGE("Error creating AudioRecord: invalid session ID pointer"); 191 return (jint) AUDIO_JAVA_ERROR; 192 } 193 194 jint* nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL); 195 if (nSession == NULL) { 196 ALOGE("Error creating AudioRecord: Error retrieving session id pointer"); 197 return (jint) AUDIO_JAVA_ERROR; 198 } 199 int sessionId = nSession[0]; 200 env->ReleasePrimitiveArrayCritical(jSession, nSession, 0); 201 nSession = NULL; 202 203 // create an uninitialized AudioRecord object 204 sp<AudioRecord> lpRecorder = new AudioRecord(); 205 206 audio_attributes_t *paa = NULL; 207 // read the AudioAttributes values 208 paa = (audio_attributes_t *) calloc(1, sizeof(audio_attributes_t)); 209 const jstring jtags = 210 (jstring) env->GetObjectField(jaa, javaAudioAttrFields.fieldFormattedTags); 211 const char* tags = env->GetStringUTFChars(jtags, NULL); 212 // copying array size -1, char array for tags was calloc'd, no need to NULL-terminate it 213 strncpy(paa->tags, tags, AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1); 214 env->ReleaseStringUTFChars(jtags, tags); 215 paa->source = (audio_source_t) env->GetIntField(jaa, javaAudioAttrFields.fieldRecSource); 216 paa->flags = (audio_flags_mask_t)env->GetIntField(jaa, javaAudioAttrFields.fieldFlags); 217 ALOGV("AudioRecord_setup for source=%d tags=%s flags=%08x", paa->source, paa->tags, paa->flags); 218 219 audio_input_flags_t flags = AUDIO_INPUT_FLAG_NONE; 220 if (paa->flags & AUDIO_FLAG_HW_HOTWORD) { 221 flags = AUDIO_INPUT_FLAG_HW_HOTWORD; 222 } 223 // create the callback information: 224 // this data will be passed with every AudioRecord callback 225 audiorecord_callback_cookie *lpCallbackData = new audiorecord_callback_cookie; 226 lpCallbackData->audioRecord_class = (jclass)env->NewGlobalRef(clazz); 227 // we use a weak reference so the AudioRecord object can be garbage collected. 228 lpCallbackData->audioRecord_ref = env->NewGlobalRef(weak_this); 229 lpCallbackData->busy = false; 230 231 const status_t status = lpRecorder->set(paa->source, 232 sampleRateInHertz, 233 format, // word length, PCM 234 channelMask, 235 frameCount, 236 recorderCallback,// callback_t 237 lpCallbackData,// void* user 238 0, // notificationFrames, 239 true, // threadCanCallJava 240 sessionId, 241 AudioRecord::TRANSFER_DEFAULT, 242 flags, 243 paa); 244 245 if (status != NO_ERROR) { 246 ALOGE("Error creating AudioRecord instance: initialization check failed with status %d.", 247 status); 248 goto native_init_failure; 249 } 250 251 nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL); 252 if (nSession == NULL) { 253 ALOGE("Error creating AudioRecord: Error retrieving session id pointer"); 254 goto native_init_failure; 255 } 256 // read the audio session ID back from AudioRecord in case a new session was created during set() 257 nSession[0] = lpRecorder->getSessionId(); 258 env->ReleasePrimitiveArrayCritical(jSession, nSession, 0); 259 nSession = NULL; 260 261 { // scope for the lock 262 Mutex::Autolock l(sLock); 263 sAudioRecordCallBackCookies.add(lpCallbackData); 264 } 265 // save our newly created C++ AudioRecord in the "nativeRecorderInJavaObj" field 266 // of the Java object 267 setAudioRecord(env, thiz, lpRecorder); 268 269 // save our newly created callback information in the "nativeCallbackCookie" field 270 // of the Java object (in mNativeCallbackCookie) so we can free the memory in finalize() 271 env->SetLongField(thiz, javaAudioRecordFields.nativeCallbackCookie, (jlong)lpCallbackData); 272 273 return (jint) AUDIO_JAVA_SUCCESS; 274 275 // failure: 276 native_init_failure: 277 env->DeleteGlobalRef(lpCallbackData->audioRecord_class); 278 env->DeleteGlobalRef(lpCallbackData->audioRecord_ref); 279 delete lpCallbackData; 280 env->SetLongField(thiz, javaAudioRecordFields.nativeCallbackCookie, 0); 281 282 return (jint) AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED; 283 } 284 285 286 287 // ---------------------------------------------------------------------------- 288 static jint 289 android_media_AudioRecord_start(JNIEnv *env, jobject thiz, jint event, jint triggerSession) 290 { 291 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); 292 if (lpRecorder == NULL ) { 293 jniThrowException(env, "java/lang/IllegalStateException", NULL); 294 return (jint) AUDIO_JAVA_ERROR; 295 } 296 297 return nativeToJavaStatus( 298 lpRecorder->start((AudioSystem::sync_event_t)event, triggerSession)); 299 } 300 301 302 // ---------------------------------------------------------------------------- 303 static void 304 android_media_AudioRecord_stop(JNIEnv *env, jobject thiz) 305 { 306 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); 307 if (lpRecorder == NULL ) { 308 jniThrowException(env, "java/lang/IllegalStateException", NULL); 309 return; 310 } 311 312 lpRecorder->stop(); 313 //ALOGV("Called lpRecorder->stop()"); 314 } 315 316 317 // ---------------------------------------------------------------------------- 318 319 #define CALLBACK_COND_WAIT_TIMEOUT_MS 1000 320 static void android_media_AudioRecord_release(JNIEnv *env, jobject thiz) { 321 sp<AudioRecord> lpRecorder = setAudioRecord(env, thiz, 0); 322 if (lpRecorder == NULL) { 323 return; 324 } 325 ALOGV("About to delete lpRecorder: %p", lpRecorder.get()); 326 lpRecorder->stop(); 327 328 audiorecord_callback_cookie *lpCookie = (audiorecord_callback_cookie *)env->GetLongField( 329 thiz, javaAudioRecordFields.nativeCallbackCookie); 330 331 // reset the native resources in the Java object so any attempt to access 332 // them after a call to release fails. 333 env->SetLongField(thiz, javaAudioRecordFields.nativeCallbackCookie, 0); 334 335 // delete the callback information 336 if (lpCookie) { 337 Mutex::Autolock l(sLock); 338 ALOGV("deleting lpCookie: %p", lpCookie); 339 while (lpCookie->busy) { 340 if (lpCookie->cond.waitRelative(sLock, 341 milliseconds(CALLBACK_COND_WAIT_TIMEOUT_MS)) != 342 NO_ERROR) { 343 break; 344 } 345 } 346 sAudioRecordCallBackCookies.remove(lpCookie); 347 env->DeleteGlobalRef(lpCookie->audioRecord_class); 348 env->DeleteGlobalRef(lpCookie->audioRecord_ref); 349 delete lpCookie; 350 } 351 } 352 353 354 // ---------------------------------------------------------------------------- 355 static void android_media_AudioRecord_finalize(JNIEnv *env, jobject thiz) { 356 android_media_AudioRecord_release(env, thiz); 357 } 358 359 360 // ---------------------------------------------------------------------------- 361 static jint android_media_AudioRecord_readInByteArray(JNIEnv *env, jobject thiz, 362 jbyteArray javaAudioData, 363 jint offsetInBytes, jint sizeInBytes) { 364 jbyte* recordBuff = NULL; 365 // get the audio recorder from which we'll read new audio samples 366 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); 367 if (lpRecorder == NULL) { 368 ALOGE("Unable to retrieve AudioRecord object, can't record"); 369 return 0; 370 } 371 372 if (!javaAudioData) { 373 ALOGE("Invalid Java array to store recorded audio, can't record"); 374 return 0; 375 } 376 377 // get the pointer to where we'll record the audio 378 // NOTE: We may use GetPrimitiveArrayCritical() when the JNI implementation changes in such 379 // a way that it becomes much more efficient. When doing so, we will have to prevent the 380 // AudioSystem callback to be called while in critical section (in case of media server 381 // process crash for instance) 382 recordBuff = (jbyte *)env->GetByteArrayElements(javaAudioData, NULL); 383 384 if (recordBuff == NULL) { 385 ALOGE("Error retrieving destination for recorded audio data, can't record"); 386 return 0; 387 } 388 389 // read the new audio data from the native AudioRecord object 390 ssize_t recorderBuffSize = lpRecorder->frameCount()*lpRecorder->frameSize(); 391 ssize_t readSize = lpRecorder->read(recordBuff + offsetInBytes, 392 sizeInBytes > (jint)recorderBuffSize ? 393 (jint)recorderBuffSize : sizeInBytes ); 394 env->ReleaseByteArrayElements(javaAudioData, recordBuff, 0); 395 396 if (readSize < 0) { 397 readSize = (jint)AUDIO_JAVA_INVALID_OPERATION; 398 } 399 return (jint) readSize; 400 } 401 402 // ---------------------------------------------------------------------------- 403 static jint android_media_AudioRecord_readInShortArray(JNIEnv *env, jobject thiz, 404 jshortArray javaAudioData, 405 jint offsetInShorts, jint sizeInShorts) { 406 407 jshort* recordBuff = NULL; 408 // get the audio recorder from which we'll read new audio samples 409 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); 410 if (lpRecorder == NULL) { 411 ALOGE("Unable to retrieve AudioRecord object, can't record"); 412 return 0; 413 } 414 415 if (!javaAudioData) { 416 ALOGE("Invalid Java array to store recorded audio, can't record"); 417 return 0; 418 } 419 420 // get the pointer to where we'll record the audio 421 // NOTE: We may use GetPrimitiveArrayCritical() when the JNI implementation changes in such 422 // a way that it becomes much more efficient. When doing so, we will have to prevent the 423 // AudioSystem callback to be called while in critical section (in case of media server 424 // process crash for instance) 425 recordBuff = (jshort *)env->GetShortArrayElements(javaAudioData, NULL); 426 427 if (recordBuff == NULL) { 428 ALOGE("Error retrieving destination for recorded audio data, can't record"); 429 return 0; 430 } 431 432 // read the new audio data from the native AudioRecord object 433 const size_t recorderBuffSize = lpRecorder->frameCount()*lpRecorder->frameSize(); 434 const size_t sizeInBytes = sizeInShorts * sizeof(short); 435 ssize_t readSize = lpRecorder->read(recordBuff + offsetInShorts, 436 sizeInBytes > recorderBuffSize ? 437 recorderBuffSize : sizeInBytes); 438 439 env->ReleaseShortArrayElements(javaAudioData, recordBuff, 0); 440 441 if (readSize < 0) { 442 readSize = (jint)AUDIO_JAVA_INVALID_OPERATION; 443 } else { 444 readSize /= sizeof(short); 445 } 446 return (jint) readSize; 447 } 448 449 // ---------------------------------------------------------------------------- 450 static jint android_media_AudioRecord_readInDirectBuffer(JNIEnv *env, jobject thiz, 451 jobject jBuffer, jint sizeInBytes) { 452 // get the audio recorder from which we'll read new audio samples 453 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); 454 if (lpRecorder==NULL) 455 return 0; 456 457 // direct buffer and direct access supported? 458 long capacity = env->GetDirectBufferCapacity(jBuffer); 459 if (capacity == -1) { 460 // buffer direct access is not supported 461 ALOGE("Buffer direct access is not supported, can't record"); 462 return 0; 463 } 464 //ALOGV("capacity = %ld", capacity); 465 jbyte* nativeFromJavaBuf = (jbyte*) env->GetDirectBufferAddress(jBuffer); 466 if (nativeFromJavaBuf==NULL) { 467 ALOGE("Buffer direct access is not supported, can't record"); 468 return 0; 469 } 470 471 // read new data from the recorder 472 ssize_t readSize = lpRecorder->read(nativeFromJavaBuf, 473 capacity < sizeInBytes ? capacity : sizeInBytes); 474 if (readSize < 0) { 475 readSize = (jint)AUDIO_JAVA_INVALID_OPERATION; 476 } 477 return (jint)readSize; 478 } 479 480 481 // ---------------------------------------------------------------------------- 482 static jint android_media_AudioRecord_set_marker_pos(JNIEnv *env, jobject thiz, 483 jint markerPos) { 484 485 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); 486 if (lpRecorder == NULL) { 487 jniThrowException(env, "java/lang/IllegalStateException", 488 "Unable to retrieve AudioRecord pointer for setMarkerPosition()"); 489 return (jint)AUDIO_JAVA_ERROR; 490 } 491 return nativeToJavaStatus( lpRecorder->setMarkerPosition(markerPos) ); 492 } 493 494 495 // ---------------------------------------------------------------------------- 496 static jint android_media_AudioRecord_get_marker_pos(JNIEnv *env, jobject thiz) { 497 498 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); 499 uint32_t markerPos = 0; 500 501 if (lpRecorder == NULL) { 502 jniThrowException(env, "java/lang/IllegalStateException", 503 "Unable to retrieve AudioRecord pointer for getMarkerPosition()"); 504 return (jint)AUDIO_JAVA_ERROR; 505 } 506 lpRecorder->getMarkerPosition(&markerPos); 507 return (jint)markerPos; 508 } 509 510 511 // ---------------------------------------------------------------------------- 512 static jint android_media_AudioRecord_set_pos_update_period(JNIEnv *env, jobject thiz, 513 jint period) { 514 515 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); 516 517 if (lpRecorder == NULL) { 518 jniThrowException(env, "java/lang/IllegalStateException", 519 "Unable to retrieve AudioRecord pointer for setPositionUpdatePeriod()"); 520 return (jint)AUDIO_JAVA_ERROR; 521 } 522 return nativeToJavaStatus( lpRecorder->setPositionUpdatePeriod(period) ); 523 } 524 525 526 // ---------------------------------------------------------------------------- 527 static jint android_media_AudioRecord_get_pos_update_period(JNIEnv *env, jobject thiz) { 528 529 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); 530 uint32_t period = 0; 531 532 if (lpRecorder == NULL) { 533 jniThrowException(env, "java/lang/IllegalStateException", 534 "Unable to retrieve AudioRecord pointer for getPositionUpdatePeriod()"); 535 return (jint)AUDIO_JAVA_ERROR; 536 } 537 lpRecorder->getPositionUpdatePeriod(&period); 538 return (jint)period; 539 } 540 541 542 // ---------------------------------------------------------------------------- 543 // returns the minimum required size for the successful creation of an AudioRecord instance. 544 // returns 0 if the parameter combination is not supported. 545 // return -1 if there was an error querying the buffer size. 546 static jint android_media_AudioRecord_get_min_buff_size(JNIEnv *env, jobject thiz, 547 jint sampleRateInHertz, jint channelCount, jint audioFormat) { 548 549 ALOGV(">> android_media_AudioRecord_get_min_buff_size(%d, %d, %d)", 550 sampleRateInHertz, channelCount, audioFormat); 551 552 size_t frameCount = 0; 553 audio_format_t format = audioFormatToNative(audioFormat); 554 status_t result = AudioRecord::getMinFrameCount(&frameCount, 555 sampleRateInHertz, 556 format, 557 audio_channel_in_mask_from_count(channelCount)); 558 559 if (result == BAD_VALUE) { 560 return 0; 561 } 562 if (result != NO_ERROR) { 563 return -1; 564 } 565 return frameCount * channelCount * audio_bytes_per_sample(format); 566 } 567 568 569 // ---------------------------------------------------------------------------- 570 // ---------------------------------------------------------------------------- 571 static JNINativeMethod gMethods[] = { 572 // name, signature, funcPtr 573 {"native_start", "(II)I", (void *)android_media_AudioRecord_start}, 574 {"native_stop", "()V", (void *)android_media_AudioRecord_stop}, 575 {"native_setup", "(Ljava/lang/Object;Ljava/lang/Object;IIII[I)I", 576 (void *)android_media_AudioRecord_setup}, 577 {"native_finalize", "()V", (void *)android_media_AudioRecord_finalize}, 578 {"native_release", "()V", (void *)android_media_AudioRecord_release}, 579 {"native_read_in_byte_array", 580 "([BII)I", (void *)android_media_AudioRecord_readInByteArray}, 581 {"native_read_in_short_array", 582 "([SII)I", (void *)android_media_AudioRecord_readInShortArray}, 583 {"native_read_in_direct_buffer","(Ljava/lang/Object;I)I", 584 (void *)android_media_AudioRecord_readInDirectBuffer}, 585 {"native_set_marker_pos","(I)I", (void *)android_media_AudioRecord_set_marker_pos}, 586 {"native_get_marker_pos","()I", (void *)android_media_AudioRecord_get_marker_pos}, 587 {"native_set_pos_update_period", 588 "(I)I", (void *)android_media_AudioRecord_set_pos_update_period}, 589 {"native_get_pos_update_period", 590 "()I", (void *)android_media_AudioRecord_get_pos_update_period}, 591 {"native_get_min_buff_size", 592 "(III)I", (void *)android_media_AudioRecord_get_min_buff_size}, 593 }; 594 595 // field names found in android/media/AudioRecord.java 596 #define JAVA_POSTEVENT_CALLBACK_NAME "postEventFromNative" 597 #define JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME "mNativeRecorderInJavaObj" 598 #define JAVA_NATIVECALLBACKINFO_FIELD_NAME "mNativeCallbackCookie" 599 600 // ---------------------------------------------------------------------------- 601 int register_android_media_AudioRecord(JNIEnv *env) 602 { 603 javaAudioRecordFields.postNativeEventInJava = NULL; 604 javaAudioRecordFields.nativeRecorderInJavaObj = NULL; 605 javaAudioRecordFields.nativeCallbackCookie = NULL; 606 607 608 // Get the AudioRecord class 609 jclass audioRecordClass = env->FindClass(kClassPathName); 610 if (audioRecordClass == NULL) { 611 ALOGE("Can't find %s", kClassPathName); 612 return -1; 613 } 614 // Get the postEvent method 615 javaAudioRecordFields.postNativeEventInJava = env->GetStaticMethodID( 616 audioRecordClass, 617 JAVA_POSTEVENT_CALLBACK_NAME, "(Ljava/lang/Object;IIILjava/lang/Object;)V"); 618 if (javaAudioRecordFields.postNativeEventInJava == NULL) { 619 ALOGE("Can't find AudioRecord.%s", JAVA_POSTEVENT_CALLBACK_NAME); 620 return -1; 621 } 622 623 // Get the variables 624 // mNativeRecorderInJavaObj 625 javaAudioRecordFields.nativeRecorderInJavaObj = 626 env->GetFieldID(audioRecordClass, 627 JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME, "J"); 628 if (javaAudioRecordFields.nativeRecorderInJavaObj == NULL) { 629 ALOGE("Can't find AudioRecord.%s", JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME); 630 return -1; 631 } 632 // mNativeCallbackCookie 633 javaAudioRecordFields.nativeCallbackCookie = env->GetFieldID( 634 audioRecordClass, 635 JAVA_NATIVECALLBACKINFO_FIELD_NAME, "J"); 636 if (javaAudioRecordFields.nativeCallbackCookie == NULL) { 637 ALOGE("Can't find AudioRecord.%s", JAVA_NATIVECALLBACKINFO_FIELD_NAME); 638 return -1; 639 } 640 641 // Get the AudioAttributes class and fields 642 jclass audioAttrClass = env->FindClass(kAudioAttributesClassPathName); 643 if (audioAttrClass == NULL) { 644 ALOGE("Can't find %s", kAudioAttributesClassPathName); 645 return -1; 646 } 647 jclass audioAttributesClassRef = (jclass)env->NewGlobalRef(audioAttrClass); 648 javaAudioAttrFields.fieldRecSource = env->GetFieldID(audioAttributesClassRef, "mSource", "I"); 649 javaAudioAttrFields.fieldFlags = env->GetFieldID(audioAttributesClassRef, "mFlags", "I"); 650 javaAudioAttrFields.fieldFormattedTags = 651 env->GetFieldID(audioAttributesClassRef, "mFormattedTags", "Ljava/lang/String;"); 652 env->DeleteGlobalRef(audioAttributesClassRef); 653 if (javaAudioAttrFields.fieldRecSource == NULL 654 || javaAudioAttrFields.fieldFlags == NULL 655 || javaAudioAttrFields.fieldFormattedTags == NULL) { 656 ALOGE("Can't initialize AudioAttributes fields"); 657 return -1; 658 } 659 660 return AndroidRuntime::registerNativeMethods(env, 661 kClassPathName, gMethods, NELEM(gMethods)); 662 } 663 664 // ---------------------------------------------------------------------------- 665