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