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 244 if (status != NO_ERROR) { 245 ALOGE("Error creating AudioRecord instance: initialization check failed with status %d.", 246 status); 247 goto native_init_failure; 248 } 249 250 nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL); 251 if (nSession == NULL) { 252 ALOGE("Error creating AudioRecord: Error retrieving session id pointer"); 253 goto native_init_failure; 254 } 255 // read the audio session ID back from AudioRecord in case a new session was created during set() 256 nSession[0] = lpRecorder->getSessionId(); 257 env->ReleasePrimitiveArrayCritical(jSession, nSession, 0); 258 nSession = NULL; 259 260 { // scope for the lock 261 Mutex::Autolock l(sLock); 262 sAudioRecordCallBackCookies.add(lpCallbackData); 263 } 264 // save our newly created C++ AudioRecord in the "nativeRecorderInJavaObj" field 265 // of the Java object 266 setAudioRecord(env, thiz, lpRecorder); 267 268 // save our newly created callback information in the "nativeCallbackCookie" field 269 // of the Java object (in mNativeCallbackCookie) so we can free the memory in finalize() 270 env->SetLongField(thiz, javaAudioRecordFields.nativeCallbackCookie, (jlong)lpCallbackData); 271 272 return (jint) AUDIO_JAVA_SUCCESS; 273 274 // failure: 275 native_init_failure: 276 env->DeleteGlobalRef(lpCallbackData->audioRecord_class); 277 env->DeleteGlobalRef(lpCallbackData->audioRecord_ref); 278 delete lpCallbackData; 279 env->SetLongField(thiz, javaAudioRecordFields.nativeCallbackCookie, 0); 280 281 return (jint) AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED; 282 } 283 284 285 286 // ---------------------------------------------------------------------------- 287 static jint 288 android_media_AudioRecord_start(JNIEnv *env, jobject thiz, jint event, jint triggerSession) 289 { 290 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); 291 if (lpRecorder == NULL ) { 292 jniThrowException(env, "java/lang/IllegalStateException", NULL); 293 return (jint) AUDIO_JAVA_ERROR; 294 } 295 296 return nativeToJavaStatus( 297 lpRecorder->start((AudioSystem::sync_event_t)event, triggerSession)); 298 } 299 300 301 // ---------------------------------------------------------------------------- 302 static void 303 android_media_AudioRecord_stop(JNIEnv *env, jobject thiz) 304 { 305 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); 306 if (lpRecorder == NULL ) { 307 jniThrowException(env, "java/lang/IllegalStateException", NULL); 308 return; 309 } 310 311 lpRecorder->stop(); 312 //ALOGV("Called lpRecorder->stop()"); 313 } 314 315 316 // ---------------------------------------------------------------------------- 317 318 #define CALLBACK_COND_WAIT_TIMEOUT_MS 1000 319 static void android_media_AudioRecord_release(JNIEnv *env, jobject thiz) { 320 sp<AudioRecord> lpRecorder = setAudioRecord(env, thiz, 0); 321 if (lpRecorder == NULL) { 322 return; 323 } 324 ALOGV("About to delete lpRecorder: %p", lpRecorder.get()); 325 lpRecorder->stop(); 326 327 audiorecord_callback_cookie *lpCookie = (audiorecord_callback_cookie *)env->GetLongField( 328 thiz, javaAudioRecordFields.nativeCallbackCookie); 329 330 // reset the native resources in the Java object so any attempt to access 331 // them after a call to release fails. 332 env->SetLongField(thiz, javaAudioRecordFields.nativeCallbackCookie, 0); 333 334 // delete the callback information 335 if (lpCookie) { 336 Mutex::Autolock l(sLock); 337 ALOGV("deleting lpCookie: %p", lpCookie); 338 while (lpCookie->busy) { 339 if (lpCookie->cond.waitRelative(sLock, 340 milliseconds(CALLBACK_COND_WAIT_TIMEOUT_MS)) != 341 NO_ERROR) { 342 break; 343 } 344 } 345 sAudioRecordCallBackCookies.remove(lpCookie); 346 env->DeleteGlobalRef(lpCookie->audioRecord_class); 347 env->DeleteGlobalRef(lpCookie->audioRecord_ref); 348 delete lpCookie; 349 } 350 } 351 352 353 // ---------------------------------------------------------------------------- 354 static void android_media_AudioRecord_finalize(JNIEnv *env, jobject thiz) { 355 android_media_AudioRecord_release(env, thiz); 356 } 357 358 359 // ---------------------------------------------------------------------------- 360 static jint android_media_AudioRecord_readInByteArray(JNIEnv *env, jobject thiz, 361 jbyteArray javaAudioData, 362 jint offsetInBytes, jint sizeInBytes) { 363 jbyte* recordBuff = NULL; 364 // get the audio recorder from which we'll read new audio samples 365 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); 366 if (lpRecorder == NULL) { 367 ALOGE("Unable to retrieve AudioRecord object, can't record"); 368 return 0; 369 } 370 371 if (!javaAudioData) { 372 ALOGE("Invalid Java array to store recorded audio, can't record"); 373 return 0; 374 } 375 376 // get the pointer to where we'll record the audio 377 // NOTE: We may use GetPrimitiveArrayCritical() when the JNI implementation changes in such 378 // a way that it becomes much more efficient. When doing so, we will have to prevent the 379 // AudioSystem callback to be called while in critical section (in case of media server 380 // process crash for instance) 381 recordBuff = (jbyte *)env->GetByteArrayElements(javaAudioData, NULL); 382 383 if (recordBuff == NULL) { 384 ALOGE("Error retrieving destination for recorded audio data, can't record"); 385 return 0; 386 } 387 388 // read the new audio data from the native AudioRecord object 389 ssize_t recorderBuffSize = lpRecorder->frameCount()*lpRecorder->frameSize(); 390 ssize_t readSize = lpRecorder->read(recordBuff + offsetInBytes, 391 sizeInBytes > (jint)recorderBuffSize ? 392 (jint)recorderBuffSize : sizeInBytes ); 393 env->ReleaseByteArrayElements(javaAudioData, recordBuff, 0); 394 395 if (readSize < 0) { 396 readSize = (jint)AUDIO_JAVA_INVALID_OPERATION; 397 } 398 return (jint) readSize; 399 } 400 401 // ---------------------------------------------------------------------------- 402 static jint android_media_AudioRecord_readInShortArray(JNIEnv *env, jobject thiz, 403 jshortArray javaAudioData, 404 jint offsetInShorts, jint sizeInShorts) { 405 406 jshort* recordBuff = NULL; 407 // get the audio recorder from which we'll read new audio samples 408 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); 409 if (lpRecorder == NULL) { 410 ALOGE("Unable to retrieve AudioRecord object, can't record"); 411 return 0; 412 } 413 414 if (!javaAudioData) { 415 ALOGE("Invalid Java array to store recorded audio, can't record"); 416 return 0; 417 } 418 419 // get the pointer to where we'll record the audio 420 // NOTE: We may use GetPrimitiveArrayCritical() when the JNI implementation changes in such 421 // a way that it becomes much more efficient. When doing so, we will have to prevent the 422 // AudioSystem callback to be called while in critical section (in case of media server 423 // process crash for instance) 424 recordBuff = (jshort *)env->GetShortArrayElements(javaAudioData, NULL); 425 426 if (recordBuff == NULL) { 427 ALOGE("Error retrieving destination for recorded audio data, can't record"); 428 return 0; 429 } 430 431 // read the new audio data from the native AudioRecord object 432 const size_t recorderBuffSize = lpRecorder->frameCount()*lpRecorder->frameSize(); 433 const size_t sizeInBytes = sizeInShorts * sizeof(short); 434 ssize_t readSize = lpRecorder->read(recordBuff + offsetInShorts * sizeof(short), 435 sizeInBytes > recorderBuffSize ? 436 recorderBuffSize : sizeInBytes); 437 438 env->ReleaseShortArrayElements(javaAudioData, recordBuff, 0); 439 440 if (readSize < 0) { 441 readSize = (jint)AUDIO_JAVA_INVALID_OPERATION; 442 } else { 443 readSize /= sizeof(short); 444 } 445 return (jint) readSize; 446 } 447 448 // ---------------------------------------------------------------------------- 449 static jint android_media_AudioRecord_readInDirectBuffer(JNIEnv *env, jobject thiz, 450 jobject jBuffer, jint sizeInBytes) { 451 // get the audio recorder from which we'll read new audio samples 452 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); 453 if (lpRecorder==NULL) 454 return 0; 455 456 // direct buffer and direct access supported? 457 long capacity = env->GetDirectBufferCapacity(jBuffer); 458 if (capacity == -1) { 459 // buffer direct access is not supported 460 ALOGE("Buffer direct access is not supported, can't record"); 461 return 0; 462 } 463 //ALOGV("capacity = %ld", capacity); 464 jbyte* nativeFromJavaBuf = (jbyte*) env->GetDirectBufferAddress(jBuffer); 465 if (nativeFromJavaBuf==NULL) { 466 ALOGE("Buffer direct access is not supported, can't record"); 467 return 0; 468 } 469 470 // read new data from the recorder 471 ssize_t readSize = lpRecorder->read(nativeFromJavaBuf, 472 capacity < sizeInBytes ? capacity : sizeInBytes); 473 if (readSize < 0) { 474 readSize = (jint)AUDIO_JAVA_INVALID_OPERATION; 475 } 476 return (jint)readSize; 477 } 478 479 480 // ---------------------------------------------------------------------------- 481 static jint android_media_AudioRecord_set_marker_pos(JNIEnv *env, jobject thiz, 482 jint markerPos) { 483 484 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); 485 if (lpRecorder == NULL) { 486 jniThrowException(env, "java/lang/IllegalStateException", 487 "Unable to retrieve AudioRecord pointer for setMarkerPosition()"); 488 return (jint)AUDIO_JAVA_ERROR; 489 } 490 return nativeToJavaStatus( lpRecorder->setMarkerPosition(markerPos) ); 491 } 492 493 494 // ---------------------------------------------------------------------------- 495 static jint android_media_AudioRecord_get_marker_pos(JNIEnv *env, jobject thiz) { 496 497 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); 498 uint32_t markerPos = 0; 499 500 if (lpRecorder == NULL) { 501 jniThrowException(env, "java/lang/IllegalStateException", 502 "Unable to retrieve AudioRecord pointer for getMarkerPosition()"); 503 return (jint)AUDIO_JAVA_ERROR; 504 } 505 lpRecorder->getMarkerPosition(&markerPos); 506 return (jint)markerPos; 507 } 508 509 510 // ---------------------------------------------------------------------------- 511 static jint android_media_AudioRecord_set_pos_update_period(JNIEnv *env, jobject thiz, 512 jint period) { 513 514 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); 515 516 if (lpRecorder == NULL) { 517 jniThrowException(env, "java/lang/IllegalStateException", 518 "Unable to retrieve AudioRecord pointer for setPositionUpdatePeriod()"); 519 return (jint)AUDIO_JAVA_ERROR; 520 } 521 return nativeToJavaStatus( lpRecorder->setPositionUpdatePeriod(period) ); 522 } 523 524 525 // ---------------------------------------------------------------------------- 526 static jint android_media_AudioRecord_get_pos_update_period(JNIEnv *env, jobject thiz) { 527 528 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); 529 uint32_t period = 0; 530 531 if (lpRecorder == NULL) { 532 jniThrowException(env, "java/lang/IllegalStateException", 533 "Unable to retrieve AudioRecord pointer for getPositionUpdatePeriod()"); 534 return (jint)AUDIO_JAVA_ERROR; 535 } 536 lpRecorder->getPositionUpdatePeriod(&period); 537 return (jint)period; 538 } 539 540 541 // ---------------------------------------------------------------------------- 542 // returns the minimum required size for the successful creation of an AudioRecord instance. 543 // returns 0 if the parameter combination is not supported. 544 // return -1 if there was an error querying the buffer size. 545 static jint android_media_AudioRecord_get_min_buff_size(JNIEnv *env, jobject thiz, 546 jint sampleRateInHertz, jint channelCount, jint audioFormat) { 547 548 ALOGV(">> android_media_AudioRecord_get_min_buff_size(%d, %d, %d)", 549 sampleRateInHertz, channelCount, audioFormat); 550 551 size_t frameCount = 0; 552 audio_format_t format = audioFormatToNative(audioFormat); 553 status_t result = AudioRecord::getMinFrameCount(&frameCount, 554 sampleRateInHertz, 555 format, 556 audio_channel_in_mask_from_count(channelCount)); 557 558 if (result == BAD_VALUE) { 559 return 0; 560 } 561 if (result != NO_ERROR) { 562 return -1; 563 } 564 return frameCount * channelCount * audio_bytes_per_sample(format); 565 } 566 567 568 // ---------------------------------------------------------------------------- 569 // ---------------------------------------------------------------------------- 570 static JNINativeMethod gMethods[] = { 571 // name, signature, funcPtr 572 {"native_start", "(II)I", (void *)android_media_AudioRecord_start}, 573 {"native_stop", "()V", (void *)android_media_AudioRecord_stop}, 574 {"native_setup", "(Ljava/lang/Object;Ljava/lang/Object;IIII[I)I", 575 (void *)android_media_AudioRecord_setup}, 576 {"native_finalize", "()V", (void *)android_media_AudioRecord_finalize}, 577 {"native_release", "()V", (void *)android_media_AudioRecord_release}, 578 {"native_read_in_byte_array", 579 "([BII)I", (void *)android_media_AudioRecord_readInByteArray}, 580 {"native_read_in_short_array", 581 "([SII)I", (void *)android_media_AudioRecord_readInShortArray}, 582 {"native_read_in_direct_buffer","(Ljava/lang/Object;I)I", 583 (void *)android_media_AudioRecord_readInDirectBuffer}, 584 {"native_set_marker_pos","(I)I", (void *)android_media_AudioRecord_set_marker_pos}, 585 {"native_get_marker_pos","()I", (void *)android_media_AudioRecord_get_marker_pos}, 586 {"native_set_pos_update_period", 587 "(I)I", (void *)android_media_AudioRecord_set_pos_update_period}, 588 {"native_get_pos_update_period", 589 "()I", (void *)android_media_AudioRecord_get_pos_update_period}, 590 {"native_get_min_buff_size", 591 "(III)I", (void *)android_media_AudioRecord_get_min_buff_size}, 592 }; 593 594 // field names found in android/media/AudioRecord.java 595 #define JAVA_POSTEVENT_CALLBACK_NAME "postEventFromNative" 596 #define JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME "mNativeRecorderInJavaObj" 597 #define JAVA_NATIVECALLBACKINFO_FIELD_NAME "mNativeCallbackCookie" 598 599 // ---------------------------------------------------------------------------- 600 int register_android_media_AudioRecord(JNIEnv *env) 601 { 602 javaAudioRecordFields.postNativeEventInJava = NULL; 603 javaAudioRecordFields.nativeRecorderInJavaObj = NULL; 604 javaAudioRecordFields.nativeCallbackCookie = NULL; 605 606 607 // Get the AudioRecord class 608 jclass audioRecordClass = env->FindClass(kClassPathName); 609 if (audioRecordClass == NULL) { 610 ALOGE("Can't find %s", kClassPathName); 611 return -1; 612 } 613 // Get the postEvent method 614 javaAudioRecordFields.postNativeEventInJava = env->GetStaticMethodID( 615 audioRecordClass, 616 JAVA_POSTEVENT_CALLBACK_NAME, "(Ljava/lang/Object;IIILjava/lang/Object;)V"); 617 if (javaAudioRecordFields.postNativeEventInJava == NULL) { 618 ALOGE("Can't find AudioRecord.%s", JAVA_POSTEVENT_CALLBACK_NAME); 619 return -1; 620 } 621 622 // Get the variables 623 // mNativeRecorderInJavaObj 624 javaAudioRecordFields.nativeRecorderInJavaObj = 625 env->GetFieldID(audioRecordClass, 626 JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME, "J"); 627 if (javaAudioRecordFields.nativeRecorderInJavaObj == NULL) { 628 ALOGE("Can't find AudioRecord.%s", JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME); 629 return -1; 630 } 631 // mNativeCallbackCookie 632 javaAudioRecordFields.nativeCallbackCookie = env->GetFieldID( 633 audioRecordClass, 634 JAVA_NATIVECALLBACKINFO_FIELD_NAME, "J"); 635 if (javaAudioRecordFields.nativeCallbackCookie == NULL) { 636 ALOGE("Can't find AudioRecord.%s", JAVA_NATIVECALLBACKINFO_FIELD_NAME); 637 return -1; 638 } 639 640 // Get the AudioAttributes class and fields 641 jclass audioAttrClass = env->FindClass(kAudioAttributesClassPathName); 642 if (audioAttrClass == NULL) { 643 ALOGE("Can't find %s", kAudioAttributesClassPathName); 644 return -1; 645 } 646 jclass audioAttributesClassRef = (jclass)env->NewGlobalRef(audioAttrClass); 647 javaAudioAttrFields.fieldRecSource = env->GetFieldID(audioAttributesClassRef, "mSource", "I"); 648 javaAudioAttrFields.fieldFlags = env->GetFieldID(audioAttributesClassRef, "mFlags", "I"); 649 javaAudioAttrFields.fieldFormattedTags = 650 env->GetFieldID(audioAttributesClassRef, "mFormattedTags", "Ljava/lang/String;"); 651 env->DeleteGlobalRef(audioAttributesClassRef); 652 if (javaAudioAttrFields.fieldRecSource == NULL 653 || javaAudioAttrFields.fieldFlags == NULL 654 || javaAudioAttrFields.fieldFormattedTags == NULL) { 655 ALOGE("Can't initialize AudioAttributes fields"); 656 return -1; 657 } 658 659 return AndroidRuntime::registerNativeMethods(env, 660 kClassPathName, gMethods, NELEM(gMethods)); 661 } 662 663 // ---------------------------------------------------------------------------- 664