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