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