android_media_AudioRecord.cpp revision f69cd6787d39188ea7bc41fe7d54314fb18eb214
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) && (uint32_t(source) != AUDIO_SOURCE_HOTWORD)) { 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 if (readSize < 0) { 391 readSize = AUDIORECORD_ERROR_INVALID_OPERATION; 392 } 393 return (jint) readSize; 394} 395 396// ---------------------------------------------------------------------------- 397static jint android_media_AudioRecord_readInShortArray(JNIEnv *env, jobject thiz, 398 jshortArray javaAudioData, 399 jint offsetInShorts, jint sizeInShorts) { 400 401 jint read = android_media_AudioRecord_readInByteArray(env, thiz, 402 (jbyteArray) javaAudioData, 403 offsetInShorts*2, sizeInShorts*2); 404 if (read > 0) { 405 read /= 2; 406 } 407 return read; 408} 409 410// ---------------------------------------------------------------------------- 411static jint android_media_AudioRecord_readInDirectBuffer(JNIEnv *env, jobject thiz, 412 jobject jBuffer, jint sizeInBytes) { 413 // get the audio recorder from which we'll read new audio samples 414 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); 415 if (lpRecorder==NULL) 416 return 0; 417 418 // direct buffer and direct access supported? 419 long capacity = env->GetDirectBufferCapacity(jBuffer); 420 if (capacity == -1) { 421 // buffer direct access is not supported 422 ALOGE("Buffer direct access is not supported, can't record"); 423 return 0; 424 } 425 //ALOGV("capacity = %ld", capacity); 426 jbyte* nativeFromJavaBuf = (jbyte*) env->GetDirectBufferAddress(jBuffer); 427 if (nativeFromJavaBuf==NULL) { 428 ALOGE("Buffer direct access is not supported, can't record"); 429 return 0; 430 } 431 432 // read new data from the recorder 433 ssize_t readSize = lpRecorder->read(nativeFromJavaBuf, 434 capacity < sizeInBytes ? capacity : sizeInBytes); 435 if (readSize < 0) { 436 readSize = AUDIORECORD_ERROR_INVALID_OPERATION; 437 } 438 return (jint)readSize; 439} 440 441 442// ---------------------------------------------------------------------------- 443static jint android_media_AudioRecord_set_marker_pos(JNIEnv *env, jobject thiz, 444 jint markerPos) { 445 446 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); 447 if (lpRecorder == NULL) { 448 jniThrowException(env, "java/lang/IllegalStateException", 449 "Unable to retrieve AudioRecord pointer for setMarkerPosition()"); 450 return AUDIORECORD_ERROR; 451 } 452 return android_media_translateRecorderErrorCode( lpRecorder->setMarkerPosition(markerPos) ); 453} 454 455 456// ---------------------------------------------------------------------------- 457static jint android_media_AudioRecord_get_marker_pos(JNIEnv *env, jobject thiz) { 458 459 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); 460 uint32_t markerPos = 0; 461 462 if (lpRecorder == NULL) { 463 jniThrowException(env, "java/lang/IllegalStateException", 464 "Unable to retrieve AudioRecord pointer for getMarkerPosition()"); 465 return AUDIORECORD_ERROR; 466 } 467 lpRecorder->getMarkerPosition(&markerPos); 468 return (jint)markerPos; 469} 470 471 472// ---------------------------------------------------------------------------- 473static jint android_media_AudioRecord_set_pos_update_period(JNIEnv *env, jobject thiz, 474 jint period) { 475 476 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); 477 478 if (lpRecorder == NULL) { 479 jniThrowException(env, "java/lang/IllegalStateException", 480 "Unable to retrieve AudioRecord pointer for setPositionUpdatePeriod()"); 481 return AUDIORECORD_ERROR; 482 } 483 return android_media_translateRecorderErrorCode( lpRecorder->setPositionUpdatePeriod(period) ); 484} 485 486 487// ---------------------------------------------------------------------------- 488static jint android_media_AudioRecord_get_pos_update_period(JNIEnv *env, jobject thiz) { 489 490 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); 491 uint32_t period = 0; 492 493 if (lpRecorder == NULL) { 494 jniThrowException(env, "java/lang/IllegalStateException", 495 "Unable to retrieve AudioRecord pointer for getPositionUpdatePeriod()"); 496 return AUDIORECORD_ERROR; 497 } 498 lpRecorder->getPositionUpdatePeriod(&period); 499 return (jint)period; 500} 501 502 503// ---------------------------------------------------------------------------- 504// returns the minimum required size for the successful creation of an AudioRecord instance. 505// returns 0 if the parameter combination is not supported. 506// return -1 if there was an error querying the buffer size. 507static jint android_media_AudioRecord_get_min_buff_size(JNIEnv *env, jobject thiz, 508 jint sampleRateInHertz, jint nbChannels, jint audioFormat) { 509 510 ALOGV(">> android_media_AudioRecord_get_min_buff_size(%d, %d, %d)", 511 sampleRateInHertz, nbChannels, audioFormat); 512 513 size_t frameCount = 0; 514 status_t result = AudioRecord::getMinFrameCount(&frameCount, 515 sampleRateInHertz, 516 (audioFormat == ENCODING_PCM_16BIT ? 517 AUDIO_FORMAT_PCM_16_BIT : AUDIO_FORMAT_PCM_8_BIT), 518 audio_channel_in_mask_from_count(nbChannels)); 519 520 if (result == BAD_VALUE) { 521 return 0; 522 } 523 if (result != NO_ERROR) { 524 return -1; 525 } 526 return frameCount * nbChannels * (audioFormat == ENCODING_PCM_16BIT ? 2 : 1); 527} 528 529 530// ---------------------------------------------------------------------------- 531// ---------------------------------------------------------------------------- 532static JNINativeMethod gMethods[] = { 533 // name, signature, funcPtr 534 {"native_start", "(II)I", (void *)android_media_AudioRecord_start}, 535 {"native_stop", "()V", (void *)android_media_AudioRecord_stop}, 536 {"native_setup", "(Ljava/lang/Object;IIIII[I)I", 537 (void *)android_media_AudioRecord_setup}, 538 {"native_finalize", "()V", (void *)android_media_AudioRecord_finalize}, 539 {"native_release", "()V", (void *)android_media_AudioRecord_release}, 540 {"native_read_in_byte_array", 541 "([BII)I", (void *)android_media_AudioRecord_readInByteArray}, 542 {"native_read_in_short_array", 543 "([SII)I", (void *)android_media_AudioRecord_readInShortArray}, 544 {"native_read_in_direct_buffer","(Ljava/lang/Object;I)I", 545 (void *)android_media_AudioRecord_readInDirectBuffer}, 546 {"native_set_marker_pos","(I)I", (void *)android_media_AudioRecord_set_marker_pos}, 547 {"native_get_marker_pos","()I", (void *)android_media_AudioRecord_get_marker_pos}, 548 {"native_set_pos_update_period", 549 "(I)I", (void *)android_media_AudioRecord_set_pos_update_period}, 550 {"native_get_pos_update_period", 551 "()I", (void *)android_media_AudioRecord_get_pos_update_period}, 552 {"native_get_min_buff_size", 553 "(III)I", (void *)android_media_AudioRecord_get_min_buff_size}, 554}; 555 556// field names found in android/media/AudioRecord.java 557#define JAVA_POSTEVENT_CALLBACK_NAME "postEventFromNative" 558#define JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME "mNativeRecorderInJavaObj" 559#define JAVA_NATIVECALLBACKINFO_FIELD_NAME "mNativeCallbackCookie" 560 561// ---------------------------------------------------------------------------- 562int register_android_media_AudioRecord(JNIEnv *env) 563{ 564 javaAudioRecordFields.postNativeEventInJava = NULL; 565 javaAudioRecordFields.nativeRecorderInJavaObj = NULL; 566 javaAudioRecordFields.nativeCallbackCookie = NULL; 567 568 569 // Get the AudioRecord class 570 jclass audioRecordClass = env->FindClass(kClassPathName); 571 if (audioRecordClass == NULL) { 572 ALOGE("Can't find %s", kClassPathName); 573 return -1; 574 } 575 // Get the postEvent method 576 javaAudioRecordFields.postNativeEventInJava = env->GetStaticMethodID( 577 audioRecordClass, 578 JAVA_POSTEVENT_CALLBACK_NAME, "(Ljava/lang/Object;IIILjava/lang/Object;)V"); 579 if (javaAudioRecordFields.postNativeEventInJava == NULL) { 580 ALOGE("Can't find AudioRecord.%s", JAVA_POSTEVENT_CALLBACK_NAME); 581 return -1; 582 } 583 584 // Get the variables 585 // mNativeRecorderInJavaObj 586 javaAudioRecordFields.nativeRecorderInJavaObj = 587 env->GetFieldID(audioRecordClass, 588 JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME, "I"); 589 if (javaAudioRecordFields.nativeRecorderInJavaObj == NULL) { 590 ALOGE("Can't find AudioRecord.%s", JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME); 591 return -1; 592 } 593 // mNativeCallbackCookie 594 javaAudioRecordFields.nativeCallbackCookie = env->GetFieldID( 595 audioRecordClass, 596 JAVA_NATIVECALLBACKINFO_FIELD_NAME, "I"); 597 if (javaAudioRecordFields.nativeCallbackCookie == NULL) { 598 ALOGE("Can't find AudioRecord.%s", JAVA_NATIVECALLBACKINFO_FIELD_NAME); 599 return -1; 600 } 601 602 return AndroidRuntime::registerNativeMethods(env, 603 kClassPathName, gMethods, NELEM(gMethods)); 604} 605 606// ---------------------------------------------------------------------------- 607