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