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