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 <stdio.h> 22#include <unistd.h> 23#include <fcntl.h> 24#include <math.h> 25 26#include <jni.h> 27#include <JNIHelp.h> 28#include <android_runtime/AndroidRuntime.h> 29 30#include <utils/Log.h> 31#include <utils/SortedVector.h> 32#include <utils/threads.h> 33#include <media/AudioRecord.h> 34#include <media/mediarecorder.h> 35 36#include <cutils/bitops.h> 37 38#include <system/audio.h> 39 40// ---------------------------------------------------------------------------- 41 42using namespace android; 43 44// ---------------------------------------------------------------------------- 45static const char* const kClassPathName = "android/media/AudioRecord"; 46 47struct fields_t { 48 // these fields provide access from C++ to the... 49 jmethodID postNativeEventInJava; //... event post callback method 50 int PCM16; //... format constants 51 int PCM8; //... format constants 52 jfieldID nativeRecorderInJavaObj; // provides access to the C++ AudioRecord object 53 jfieldID nativeCallbackCookie; // provides access to the AudioRecord callback data 54}; 55static fields_t javaAudioRecordFields; 56 57struct audiorecord_callback_cookie { 58 jclass audioRecord_class; 59 jobject audioRecord_ref; 60 bool busy; 61 Condition cond; 62}; 63 64static Mutex sLock; 65static SortedVector <audiorecord_callback_cookie *> sAudioRecordCallBackCookies; 66 67// ---------------------------------------------------------------------------- 68 69#define AUDIORECORD_SUCCESS 0 70#define AUDIORECORD_ERROR -1 71#define AUDIORECORD_ERROR_BAD_VALUE -2 72#define AUDIORECORD_ERROR_INVALID_OPERATION -3 73#define AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT -16 74#define AUDIORECORD_ERROR_SETUP_INVALIDCHANNELMASK -17 75#define AUDIORECORD_ERROR_SETUP_INVALIDFORMAT -18 76#define AUDIORECORD_ERROR_SETUP_INVALIDSOURCE -19 77#define AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED -20 78 79jint android_media_translateRecorderErrorCode(int code) { 80 switch (code) { 81 case NO_ERROR: 82 return AUDIORECORD_SUCCESS; 83 case BAD_VALUE: 84 return AUDIORECORD_ERROR_BAD_VALUE; 85 case INVALID_OPERATION: 86 return AUDIORECORD_ERROR_INVALID_OPERATION; 87 default: 88 return AUDIORECORD_ERROR; 89 } 90} 91 92 93// ---------------------------------------------------------------------------- 94static void recorderCallback(int event, void* user, void *info) { 95 96 audiorecord_callback_cookie *callbackInfo = (audiorecord_callback_cookie *)user; 97 { 98 Mutex::Autolock l(sLock); 99 if (sAudioRecordCallBackCookies.indexOf(callbackInfo) < 0) { 100 return; 101 } 102 callbackInfo->busy = true; 103 } 104 if (event == AudioRecord::EVENT_MORE_DATA) { 105 // set size to 0 to signal we're not using the callback to read more data 106 AudioRecord::Buffer* pBuff = (AudioRecord::Buffer*)info; 107 pBuff->size = 0; 108 109 } else if (event == AudioRecord::EVENT_MARKER) { 110 JNIEnv *env = AndroidRuntime::getJNIEnv(); 111 if (user && env) { 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 122 } else if (event == AudioRecord::EVENT_NEW_POS) { 123 JNIEnv *env = AndroidRuntime::getJNIEnv(); 124 if (user && env) { 125 env->CallStaticVoidMethod( 126 callbackInfo->audioRecord_class, 127 javaAudioRecordFields.postNativeEventInJava, 128 callbackInfo->audioRecord_ref, event, 0,0, NULL); 129 if (env->ExceptionCheck()) { 130 env->ExceptionDescribe(); 131 env->ExceptionClear(); 132 } 133 } 134 } 135 { 136 Mutex::Autolock l(sLock); 137 callbackInfo->busy = false; 138 callbackInfo->cond.broadcast(); 139 } 140} 141 142// ---------------------------------------------------------------------------- 143static sp<AudioRecord> getAudioRecord(JNIEnv* env, jobject thiz) 144{ 145 Mutex::Autolock l(sLock); 146 AudioRecord* const ar = 147 (AudioRecord*)env->GetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj); 148 return sp<AudioRecord>(ar); 149} 150 151static sp<AudioRecord> setAudioRecord(JNIEnv* env, jobject thiz, const sp<AudioRecord>& ar) 152{ 153 Mutex::Autolock l(sLock); 154 sp<AudioRecord> old = 155 (AudioRecord*)env->GetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj); 156 if (ar.get()) { 157 ar->incStrong(thiz); 158 } 159 if (old != 0) { 160 old->decStrong(thiz); 161 } 162 env->SetIntField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj, (int)ar.get()); 163 return old; 164} 165 166// ---------------------------------------------------------------------------- 167static int 168android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject weak_this, 169 jint source, jint sampleRateInHertz, jint channels, 170 jint audioFormat, jint buffSizeInBytes, jintArray jSession) 171{ 172 //ALOGV(">> Entering android_media_AudioRecord_setup"); 173 //ALOGV("sampleRate=%d, audioFormat=%d, channels=%x, buffSizeInBytes=%d", 174 // sampleRateInHertz, audioFormat, channels, buffSizeInBytes); 175 176 if (!audio_is_input_channel(channels)) { 177 ALOGE("Error creating AudioRecord: channel count is not 1 or 2."); 178 return AUDIORECORD_ERROR_SETUP_INVALIDCHANNELMASK; 179 } 180 uint32_t nbChannels = popcount(channels); 181 182 // compare the format against the Java constants 183 if ((audioFormat != javaAudioRecordFields.PCM16) 184 && (audioFormat != javaAudioRecordFields.PCM8)) { 185 ALOGE("Error creating AudioRecord: unsupported audio format."); 186 return AUDIORECORD_ERROR_SETUP_INVALIDFORMAT; 187 } 188 189 int bytesPerSample = audioFormat==javaAudioRecordFields.PCM16 ? 2 : 1; 190 audio_format_t format = audioFormat==javaAudioRecordFields.PCM16 ? 191 AUDIO_FORMAT_PCM_16_BIT : AUDIO_FORMAT_PCM_8_BIT; 192 193 if (buffSizeInBytes == 0) { 194 ALOGE("Error creating AudioRecord: frameCount is 0."); 195 return AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT; 196 } 197 int frameSize = nbChannels * bytesPerSample; 198 size_t frameCount = buffSizeInBytes / frameSize; 199 200 if (uint32_t(source) >= AUDIO_SOURCE_CNT) { 201 ALOGE("Error creating AudioRecord: unknown source."); 202 return AUDIORECORD_ERROR_SETUP_INVALIDSOURCE; 203 } 204 205 jclass clazz = env->GetObjectClass(thiz); 206 if (clazz == NULL) { 207 ALOGE("Can't find %s when setting up callback.", kClassPathName); 208 return AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED; 209 } 210 211 if (jSession == NULL) { 212 ALOGE("Error creating AudioRecord: invalid session ID pointer"); 213 return AUDIORECORD_ERROR; 214 } 215 216 jint* nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL); 217 if (nSession == NULL) { 218 ALOGE("Error creating AudioRecord: Error retrieving session id pointer"); 219 return AUDIORECORD_ERROR; 220 } 221 int sessionId = nSession[0]; 222 env->ReleasePrimitiveArrayCritical(jSession, nSession, 0); 223 nSession = NULL; 224 225 // create an uninitialized AudioRecord object 226 sp<AudioRecord> lpRecorder = new AudioRecord(); 227 if (lpRecorder == NULL) { 228 ALOGE("Error creating AudioRecord instance."); 229 return AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED; 230 } 231 232 // create the callback information: 233 // this data will be passed with every AudioRecord callback 234 audiorecord_callback_cookie *lpCallbackData = new audiorecord_callback_cookie; 235 lpCallbackData->audioRecord_class = (jclass)env->NewGlobalRef(clazz); 236 // we use a weak reference so the AudioRecord object can be garbage collected. 237 lpCallbackData->audioRecord_ref = env->NewGlobalRef(weak_this); 238 lpCallbackData->busy = false; 239 240 lpRecorder->set((audio_source_t) source, 241 sampleRateInHertz, 242 format, // word length, PCM 243 channels, 244 frameCount, 245 recorderCallback,// callback_t 246 lpCallbackData,// void* user 247 0, // notificationFrames, 248 true, // threadCanCallJava) 249 sessionId); 250 251 if (lpRecorder->initCheck() != NO_ERROR) { 252 ALOGE("Error creating AudioRecord instance: initialization check failed."); 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 return (jint) readSize; 402} 403 404// ---------------------------------------------------------------------------- 405static jint android_media_AudioRecord_readInShortArray(JNIEnv *env, jobject thiz, 406 jshortArray javaAudioData, 407 jint offsetInShorts, jint sizeInShorts) { 408 409 return (android_media_AudioRecord_readInByteArray(env, thiz, 410 (jbyteArray) javaAudioData, 411 offsetInShorts*2, sizeInShorts*2) 412 / 2); 413} 414 415// ---------------------------------------------------------------------------- 416static jint android_media_AudioRecord_readInDirectBuffer(JNIEnv *env, jobject thiz, 417 jobject jBuffer, jint sizeInBytes) { 418 // get the audio recorder from which we'll read new audio samples 419 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); 420 if (lpRecorder==NULL) 421 return 0; 422 423 // direct buffer and direct access supported? 424 long capacity = env->GetDirectBufferCapacity(jBuffer); 425 if (capacity == -1) { 426 // buffer direct access is not supported 427 ALOGE("Buffer direct access is not supported, can't record"); 428 return 0; 429 } 430 //ALOGV("capacity = %ld", capacity); 431 jbyte* nativeFromJavaBuf = (jbyte*) env->GetDirectBufferAddress(jBuffer); 432 if (nativeFromJavaBuf==NULL) { 433 ALOGE("Buffer direct access is not supported, can't record"); 434 return 0; 435 } 436 437 // read new data from the recorder 438 return (jint) lpRecorder->read(nativeFromJavaBuf, 439 capacity < sizeInBytes ? capacity : sizeInBytes); 440} 441 442 443// ---------------------------------------------------------------------------- 444static jint android_media_AudioRecord_set_marker_pos(JNIEnv *env, jobject thiz, 445 jint markerPos) { 446 447 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); 448 if (lpRecorder == NULL) { 449 jniThrowException(env, "java/lang/IllegalStateException", 450 "Unable to retrieve AudioRecord pointer for setMarkerPosition()"); 451 return AUDIORECORD_ERROR; 452 } 453 return android_media_translateRecorderErrorCode( lpRecorder->setMarkerPosition(markerPos) ); 454} 455 456 457// ---------------------------------------------------------------------------- 458static jint android_media_AudioRecord_get_marker_pos(JNIEnv *env, jobject thiz) { 459 460 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); 461 uint32_t markerPos = 0; 462 463 if (lpRecorder == NULL) { 464 jniThrowException(env, "java/lang/IllegalStateException", 465 "Unable to retrieve AudioRecord pointer for getMarkerPosition()"); 466 return AUDIORECORD_ERROR; 467 } 468 lpRecorder->getMarkerPosition(&markerPos); 469 return (jint)markerPos; 470} 471 472 473// ---------------------------------------------------------------------------- 474static jint android_media_AudioRecord_set_pos_update_period(JNIEnv *env, jobject thiz, 475 jint period) { 476 477 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); 478 479 if (lpRecorder == NULL) { 480 jniThrowException(env, "java/lang/IllegalStateException", 481 "Unable to retrieve AudioRecord pointer for setPositionUpdatePeriod()"); 482 return AUDIORECORD_ERROR; 483 } 484 return android_media_translateRecorderErrorCode( lpRecorder->setPositionUpdatePeriod(period) ); 485} 486 487 488// ---------------------------------------------------------------------------- 489static jint android_media_AudioRecord_get_pos_update_period(JNIEnv *env, jobject thiz) { 490 491 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); 492 uint32_t period = 0; 493 494 if (lpRecorder == NULL) { 495 jniThrowException(env, "java/lang/IllegalStateException", 496 "Unable to retrieve AudioRecord pointer for getPositionUpdatePeriod()"); 497 return AUDIORECORD_ERROR; 498 } 499 lpRecorder->getPositionUpdatePeriod(&period); 500 return (jint)period; 501} 502 503 504// ---------------------------------------------------------------------------- 505// returns the minimum required size for the successful creation of an AudioRecord instance. 506// returns 0 if the parameter combination is not supported. 507// return -1 if there was an error querying the buffer size. 508static jint android_media_AudioRecord_get_min_buff_size(JNIEnv *env, jobject thiz, 509 jint sampleRateInHertz, jint nbChannels, jint audioFormat) { 510 511 ALOGV(">> android_media_AudioRecord_get_min_buff_size(%d, %d, %d)", 512 sampleRateInHertz, nbChannels, audioFormat); 513 514 int frameCount = 0; 515 status_t result = AudioRecord::getMinFrameCount(&frameCount, 516 sampleRateInHertz, 517 (audioFormat == javaAudioRecordFields.PCM16 ? 518 AUDIO_FORMAT_PCM_16_BIT : AUDIO_FORMAT_PCM_8_BIT), 519 audio_channel_in_mask_from_count(nbChannels)); 520 521 if (result == BAD_VALUE) { 522 return 0; 523 } 524 if (result != NO_ERROR) { 525 return -1; 526 } 527 return frameCount * nbChannels * (audioFormat == javaAudioRecordFields.PCM16 ? 2 : 1); 528} 529 530 531// ---------------------------------------------------------------------------- 532// ---------------------------------------------------------------------------- 533static JNINativeMethod gMethods[] = { 534 // name, signature, funcPtr 535 {"native_start", "(II)I", (void *)android_media_AudioRecord_start}, 536 {"native_stop", "()V", (void *)android_media_AudioRecord_stop}, 537 {"native_setup", "(Ljava/lang/Object;IIIII[I)I", 538 (void *)android_media_AudioRecord_setup}, 539 {"native_finalize", "()V", (void *)android_media_AudioRecord_finalize}, 540 {"native_release", "()V", (void *)android_media_AudioRecord_release}, 541 {"native_read_in_byte_array", 542 "([BII)I", (void *)android_media_AudioRecord_readInByteArray}, 543 {"native_read_in_short_array", 544 "([SII)I", (void *)android_media_AudioRecord_readInShortArray}, 545 {"native_read_in_direct_buffer","(Ljava/lang/Object;I)I", 546 (void *)android_media_AudioRecord_readInDirectBuffer}, 547 {"native_set_marker_pos","(I)I", (void *)android_media_AudioRecord_set_marker_pos}, 548 {"native_get_marker_pos","()I", (void *)android_media_AudioRecord_get_marker_pos}, 549 {"native_set_pos_update_period", 550 "(I)I", (void *)android_media_AudioRecord_set_pos_update_period}, 551 {"native_get_pos_update_period", 552 "()I", (void *)android_media_AudioRecord_get_pos_update_period}, 553 {"native_get_min_buff_size", 554 "(III)I", (void *)android_media_AudioRecord_get_min_buff_size}, 555}; 556 557// field names found in android/media/AudioRecord.java 558#define JAVA_POSTEVENT_CALLBACK_NAME "postEventFromNative" 559#define JAVA_CONST_PCM16_NAME "ENCODING_PCM_16BIT" 560#define JAVA_CONST_PCM8_NAME "ENCODING_PCM_8BIT" 561#define JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME "mNativeRecorderInJavaObj" 562#define JAVA_NATIVECALLBACKINFO_FIELD_NAME "mNativeCallbackCookie" 563 564#define JAVA_AUDIOFORMAT_CLASS_NAME "android/media/AudioFormat" 565 566// ---------------------------------------------------------------------------- 567 568extern bool android_media_getIntConstantFromClass(JNIEnv* pEnv, 569 jclass theClass, const char* className, const char* constName, int* constVal); 570 571// ---------------------------------------------------------------------------- 572int register_android_media_AudioRecord(JNIEnv *env) 573{ 574 javaAudioRecordFields.postNativeEventInJava = NULL; 575 javaAudioRecordFields.nativeRecorderInJavaObj = NULL; 576 javaAudioRecordFields.nativeCallbackCookie = NULL; 577 578 579 // Get the AudioRecord class 580 jclass audioRecordClass = env->FindClass(kClassPathName); 581 if (audioRecordClass == NULL) { 582 ALOGE("Can't find %s", kClassPathName); 583 return -1; 584 } 585 // Get the postEvent method 586 javaAudioRecordFields.postNativeEventInJava = env->GetStaticMethodID( 587 audioRecordClass, 588 JAVA_POSTEVENT_CALLBACK_NAME, "(Ljava/lang/Object;IIILjava/lang/Object;)V"); 589 if (javaAudioRecordFields.postNativeEventInJava == NULL) { 590 ALOGE("Can't find AudioRecord.%s", JAVA_POSTEVENT_CALLBACK_NAME); 591 return -1; 592 } 593 594 // Get the variables 595 // mNativeRecorderInJavaObj 596 javaAudioRecordFields.nativeRecorderInJavaObj = 597 env->GetFieldID(audioRecordClass, 598 JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME, "I"); 599 if (javaAudioRecordFields.nativeRecorderInJavaObj == NULL) { 600 ALOGE("Can't find AudioRecord.%s", JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME); 601 return -1; 602 } 603 // mNativeCallbackCookie 604 javaAudioRecordFields.nativeCallbackCookie = env->GetFieldID( 605 audioRecordClass, 606 JAVA_NATIVECALLBACKINFO_FIELD_NAME, "I"); 607 if (javaAudioRecordFields.nativeCallbackCookie == NULL) { 608 ALOGE("Can't find AudioRecord.%s", JAVA_NATIVECALLBACKINFO_FIELD_NAME); 609 return -1; 610 } 611 612 // Get the format constants from the AudioFormat class 613 jclass audioFormatClass = NULL; 614 audioFormatClass = env->FindClass(JAVA_AUDIOFORMAT_CLASS_NAME); 615 if (audioFormatClass == NULL) { 616 ALOGE("Can't find %s", JAVA_AUDIOFORMAT_CLASS_NAME); 617 return -1; 618 } 619 if ( !android_media_getIntConstantFromClass(env, audioFormatClass, 620 JAVA_AUDIOFORMAT_CLASS_NAME, 621 JAVA_CONST_PCM16_NAME, &(javaAudioRecordFields.PCM16)) 622 || !android_media_getIntConstantFromClass(env, audioFormatClass, 623 JAVA_AUDIOFORMAT_CLASS_NAME, 624 JAVA_CONST_PCM8_NAME, &(javaAudioRecordFields.PCM8)) ) { 625 // error log performed in getIntConstantFromClass() 626 return -1; 627 } 628 629 return AndroidRuntime::registerNativeMethods(env, 630 kClassPathName, gMethods, NELEM(gMethods)); 631} 632 633// ---------------------------------------------------------------------------- 634