android_media_AudioRecord.cpp revision 8d87d7ba113ad90685f1d2669e65251a9b422a5a
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// This class is used to destroy a RefBase asynchronously 314class AsyncDestructThread : public Thread 315{ 316public: 317 AsyncDestructThread(sp<RefBase> refBase) : mRefBase(refBase) { } 318protected: 319 virtual ~AsyncDestructThread() { } 320private: 321 virtual bool threadLoop() { return false; } 322 const sp<RefBase> mRefBase; 323}; 324 325#define CALLBACK_COND_WAIT_TIMEOUT_MS 1000 326static void android_media_AudioRecord_release(JNIEnv *env, jobject thiz) { 327 sp<AudioRecord> lpRecorder = setAudioRecord(env, thiz, 0); 328 if (lpRecorder == NULL) { 329 return; 330 } 331 ALOGV("About to delete lpRecorder: %x\n", (int)lpRecorder.get()); 332 lpRecorder->stop(); 333 334 audiorecord_callback_cookie *lpCookie = (audiorecord_callback_cookie *)env->GetIntField( 335 thiz, javaAudioRecordFields.nativeCallbackCookie); 336 337 // reset the native resources in the Java object so any attempt to access 338 // them after a call to release fails. 339 env->SetIntField(thiz, javaAudioRecordFields.nativeCallbackCookie, 0); 340 341 // delete the callback information 342 if (lpCookie) { 343 Mutex::Autolock l(sLock); 344 ALOGV("deleting lpCookie: %x\n", (int)lpCookie); 345 while (lpCookie->busy) { 346 if (lpCookie->cond.waitRelative(sLock, 347 milliseconds(CALLBACK_COND_WAIT_TIMEOUT_MS)) != 348 NO_ERROR) { 349 break; 350 } 351 } 352 sAudioRecordCallBackCookies.remove(lpCookie); 353 env->DeleteGlobalRef(lpCookie->audioRecord_class); 354 env->DeleteGlobalRef(lpCookie->audioRecord_ref); 355 delete lpCookie; 356 } 357 // FIXME AudioRecord destruction should not be slow 358 if (lpRecorder != 0) { 359 // must be a raw reference to avoid a race after run() 360 AsyncDestructThread *adt = new AsyncDestructThread(lpRecorder); 361 // guaranteed to not run destructor 362 lpRecorder.clear(); 363 // after the run(), adt thread will hold a strong reference to lpRecorder, 364 // and the only strong reference to itself 365 adt->run("AsyncDestruct"); 366 // do not delete adt here: adt thread destroys itself, and lpRecorder if needed 367 } 368} 369 370 371// ---------------------------------------------------------------------------- 372static void android_media_AudioRecord_finalize(JNIEnv *env, jobject thiz) { 373 android_media_AudioRecord_release(env, thiz); 374} 375 376 377// ---------------------------------------------------------------------------- 378static jint android_media_AudioRecord_readInByteArray(JNIEnv *env, jobject thiz, 379 jbyteArray javaAudioData, 380 jint offsetInBytes, jint sizeInBytes) { 381 jbyte* recordBuff = NULL; 382 // get the audio recorder from which we'll read new audio samples 383 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); 384 if (lpRecorder == NULL) { 385 ALOGE("Unable to retrieve AudioRecord object, can't record"); 386 return 0; 387 } 388 389 if (!javaAudioData) { 390 ALOGE("Invalid Java array to store recorded audio, can't record"); 391 return 0; 392 } 393 394 // get the pointer to where we'll record the audio 395 // NOTE: We may use GetPrimitiveArrayCritical() when the JNI implementation changes in such 396 // a way that it becomes much more efficient. When doing so, we will have to prevent the 397 // AudioSystem callback to be called while in critical section (in case of media server 398 // process crash for instance) 399 recordBuff = (jbyte *)env->GetByteArrayElements(javaAudioData, NULL); 400 401 if (recordBuff == NULL) { 402 ALOGE("Error retrieving destination for recorded audio data, can't record"); 403 return 0; 404 } 405 406 // read the new audio data from the native AudioRecord object 407 ssize_t recorderBuffSize = lpRecorder->frameCount()*lpRecorder->frameSize(); 408 ssize_t readSize = lpRecorder->read(recordBuff + offsetInBytes, 409 sizeInBytes > (jint)recorderBuffSize ? 410 (jint)recorderBuffSize : sizeInBytes ); 411 env->ReleaseByteArrayElements(javaAudioData, recordBuff, 0); 412 413 if (readSize < 0) { 414 readSize = AUDIORECORD_ERROR_INVALID_OPERATION; 415 } 416 return (jint) readSize; 417} 418 419// ---------------------------------------------------------------------------- 420static jint android_media_AudioRecord_readInShortArray(JNIEnv *env, jobject thiz, 421 jshortArray javaAudioData, 422 jint offsetInShorts, jint sizeInShorts) { 423 424 jint read = android_media_AudioRecord_readInByteArray(env, thiz, 425 (jbyteArray) javaAudioData, 426 offsetInShorts*2, sizeInShorts*2); 427 if (read > 0) { 428 read /= 2; 429 } 430 return read; 431} 432 433// ---------------------------------------------------------------------------- 434static jint android_media_AudioRecord_readInDirectBuffer(JNIEnv *env, jobject thiz, 435 jobject jBuffer, jint sizeInBytes) { 436 // get the audio recorder from which we'll read new audio samples 437 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); 438 if (lpRecorder==NULL) 439 return 0; 440 441 // direct buffer and direct access supported? 442 long capacity = env->GetDirectBufferCapacity(jBuffer); 443 if (capacity == -1) { 444 // buffer direct access is not supported 445 ALOGE("Buffer direct access is not supported, can't record"); 446 return 0; 447 } 448 //ALOGV("capacity = %ld", capacity); 449 jbyte* nativeFromJavaBuf = (jbyte*) env->GetDirectBufferAddress(jBuffer); 450 if (nativeFromJavaBuf==NULL) { 451 ALOGE("Buffer direct access is not supported, can't record"); 452 return 0; 453 } 454 455 // read new data from the recorder 456 ssize_t readSize = lpRecorder->read(nativeFromJavaBuf, 457 capacity < sizeInBytes ? capacity : sizeInBytes); 458 if (readSize < 0) { 459 readSize = AUDIORECORD_ERROR_INVALID_OPERATION; 460 } 461 return (jint)readSize; 462} 463 464 465// ---------------------------------------------------------------------------- 466static jint android_media_AudioRecord_set_marker_pos(JNIEnv *env, jobject thiz, 467 jint markerPos) { 468 469 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); 470 if (lpRecorder == NULL) { 471 jniThrowException(env, "java/lang/IllegalStateException", 472 "Unable to retrieve AudioRecord pointer for setMarkerPosition()"); 473 return AUDIORECORD_ERROR; 474 } 475 return android_media_translateRecorderErrorCode( lpRecorder->setMarkerPosition(markerPos) ); 476} 477 478 479// ---------------------------------------------------------------------------- 480static jint android_media_AudioRecord_get_marker_pos(JNIEnv *env, jobject thiz) { 481 482 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); 483 uint32_t markerPos = 0; 484 485 if (lpRecorder == NULL) { 486 jniThrowException(env, "java/lang/IllegalStateException", 487 "Unable to retrieve AudioRecord pointer for getMarkerPosition()"); 488 return AUDIORECORD_ERROR; 489 } 490 lpRecorder->getMarkerPosition(&markerPos); 491 return (jint)markerPos; 492} 493 494 495// ---------------------------------------------------------------------------- 496static jint android_media_AudioRecord_set_pos_update_period(JNIEnv *env, jobject thiz, 497 jint period) { 498 499 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); 500 501 if (lpRecorder == NULL) { 502 jniThrowException(env, "java/lang/IllegalStateException", 503 "Unable to retrieve AudioRecord pointer for setPositionUpdatePeriod()"); 504 return AUDIORECORD_ERROR; 505 } 506 return android_media_translateRecorderErrorCode( lpRecorder->setPositionUpdatePeriod(period) ); 507} 508 509 510// ---------------------------------------------------------------------------- 511static jint android_media_AudioRecord_get_pos_update_period(JNIEnv *env, jobject thiz) { 512 513 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); 514 uint32_t period = 0; 515 516 if (lpRecorder == NULL) { 517 jniThrowException(env, "java/lang/IllegalStateException", 518 "Unable to retrieve AudioRecord pointer for getPositionUpdatePeriod()"); 519 return AUDIORECORD_ERROR; 520 } 521 lpRecorder->getPositionUpdatePeriod(&period); 522 return (jint)period; 523} 524 525 526// ---------------------------------------------------------------------------- 527// returns the minimum required size for the successful creation of an AudioRecord instance. 528// returns 0 if the parameter combination is not supported. 529// return -1 if there was an error querying the buffer size. 530static jint android_media_AudioRecord_get_min_buff_size(JNIEnv *env, jobject thiz, 531 jint sampleRateInHertz, jint nbChannels, jint audioFormat) { 532 533 ALOGV(">> android_media_AudioRecord_get_min_buff_size(%d, %d, %d)", 534 sampleRateInHertz, nbChannels, audioFormat); 535 536 size_t frameCount = 0; 537 status_t result = AudioRecord::getMinFrameCount(&frameCount, 538 sampleRateInHertz, 539 (audioFormat == ENCODING_PCM_16BIT ? 540 AUDIO_FORMAT_PCM_16_BIT : AUDIO_FORMAT_PCM_8_BIT), 541 audio_channel_in_mask_from_count(nbChannels)); 542 543 if (result == BAD_VALUE) { 544 return 0; 545 } 546 if (result != NO_ERROR) { 547 return -1; 548 } 549 return frameCount * nbChannels * (audioFormat == ENCODING_PCM_16BIT ? 2 : 1); 550} 551 552 553// ---------------------------------------------------------------------------- 554// ---------------------------------------------------------------------------- 555static JNINativeMethod gMethods[] = { 556 // name, signature, funcPtr 557 {"native_start", "(II)I", (void *)android_media_AudioRecord_start}, 558 {"native_stop", "()V", (void *)android_media_AudioRecord_stop}, 559 {"native_setup", "(Ljava/lang/Object;IIIII[I)I", 560 (void *)android_media_AudioRecord_setup}, 561 {"native_finalize", "()V", (void *)android_media_AudioRecord_finalize}, 562 {"native_release", "()V", (void *)android_media_AudioRecord_release}, 563 {"native_read_in_byte_array", 564 "([BII)I", (void *)android_media_AudioRecord_readInByteArray}, 565 {"native_read_in_short_array", 566 "([SII)I", (void *)android_media_AudioRecord_readInShortArray}, 567 {"native_read_in_direct_buffer","(Ljava/lang/Object;I)I", 568 (void *)android_media_AudioRecord_readInDirectBuffer}, 569 {"native_set_marker_pos","(I)I", (void *)android_media_AudioRecord_set_marker_pos}, 570 {"native_get_marker_pos","()I", (void *)android_media_AudioRecord_get_marker_pos}, 571 {"native_set_pos_update_period", 572 "(I)I", (void *)android_media_AudioRecord_set_pos_update_period}, 573 {"native_get_pos_update_period", 574 "()I", (void *)android_media_AudioRecord_get_pos_update_period}, 575 {"native_get_min_buff_size", 576 "(III)I", (void *)android_media_AudioRecord_get_min_buff_size}, 577}; 578 579// field names found in android/media/AudioRecord.java 580#define JAVA_POSTEVENT_CALLBACK_NAME "postEventFromNative" 581#define JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME "mNativeRecorderInJavaObj" 582#define JAVA_NATIVECALLBACKINFO_FIELD_NAME "mNativeCallbackCookie" 583 584// ---------------------------------------------------------------------------- 585int register_android_media_AudioRecord(JNIEnv *env) 586{ 587 javaAudioRecordFields.postNativeEventInJava = NULL; 588 javaAudioRecordFields.nativeRecorderInJavaObj = NULL; 589 javaAudioRecordFields.nativeCallbackCookie = NULL; 590 591 592 // Get the AudioRecord class 593 jclass audioRecordClass = env->FindClass(kClassPathName); 594 if (audioRecordClass == NULL) { 595 ALOGE("Can't find %s", kClassPathName); 596 return -1; 597 } 598 // Get the postEvent method 599 javaAudioRecordFields.postNativeEventInJava = env->GetStaticMethodID( 600 audioRecordClass, 601 JAVA_POSTEVENT_CALLBACK_NAME, "(Ljava/lang/Object;IIILjava/lang/Object;)V"); 602 if (javaAudioRecordFields.postNativeEventInJava == NULL) { 603 ALOGE("Can't find AudioRecord.%s", JAVA_POSTEVENT_CALLBACK_NAME); 604 return -1; 605 } 606 607 // Get the variables 608 // mNativeRecorderInJavaObj 609 javaAudioRecordFields.nativeRecorderInJavaObj = 610 env->GetFieldID(audioRecordClass, 611 JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME, "I"); 612 if (javaAudioRecordFields.nativeRecorderInJavaObj == NULL) { 613 ALOGE("Can't find AudioRecord.%s", JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME); 614 return -1; 615 } 616 // mNativeCallbackCookie 617 javaAudioRecordFields.nativeCallbackCookie = env->GetFieldID( 618 audioRecordClass, 619 JAVA_NATIVECALLBACKINFO_FIELD_NAME, "I"); 620 if (javaAudioRecordFields.nativeCallbackCookie == NULL) { 621 ALOGE("Can't find AudioRecord.%s", JAVA_NATIVECALLBACKINFO_FIELD_NAME); 622 return -1; 623 } 624 625 return AndroidRuntime::registerNativeMethods(env, 626 kClassPathName, gMethods, NELEM(gMethods)); 627} 628 629// ---------------------------------------------------------------------------- 630