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 (AudioRecord::record_flags) 0, // flags 246 recorderCallback,// callback_t 247 lpCallbackData,// void* user 248 0, // notificationFrames, 249 true, // threadCanCallJava) 250 sessionId); 251 252 if (lpRecorder->initCheck() != NO_ERROR) { 253 ALOGE("Error creating AudioRecord instance: initialization check failed."); 254 goto native_init_failure; 255 } 256 257 nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL); 258 if (nSession == NULL) { 259 ALOGE("Error creating AudioRecord: Error retrieving session id pointer"); 260 goto native_init_failure; 261 } 262 // read the audio session ID back from AudioRecord in case a new session was created during set() 263 nSession[0] = lpRecorder->getSessionId(); 264 env->ReleasePrimitiveArrayCritical(jSession, nSession, 0); 265 nSession = NULL; 266 267 { // scope for the lock 268 Mutex::Autolock l(sLock); 269 sAudioRecordCallBackCookies.add(lpCallbackData); 270 } 271 // save our newly created C++ AudioRecord in the "nativeRecorderInJavaObj" field 272 // of the Java object 273 setAudioRecord(env, thiz, lpRecorder); 274 275 // save our newly created callback information in the "nativeCallbackCookie" field 276 // of the Java object (in mNativeCallbackCookie) so we can free the memory in finalize() 277 env->SetIntField(thiz, javaAudioRecordFields.nativeCallbackCookie, (int)lpCallbackData); 278 279 return AUDIORECORD_SUCCESS; 280 281 // failure: 282native_init_failure: 283 env->DeleteGlobalRef(lpCallbackData->audioRecord_class); 284 env->DeleteGlobalRef(lpCallbackData->audioRecord_ref); 285 delete lpCallbackData; 286 env->SetIntField(thiz, javaAudioRecordFields.nativeCallbackCookie, 0); 287 288 return AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED; 289} 290 291 292 293// ---------------------------------------------------------------------------- 294static int 295android_media_AudioRecord_start(JNIEnv *env, jobject thiz, jint event, jint triggerSession) 296{ 297 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); 298 if (lpRecorder == NULL ) { 299 jniThrowException(env, "java/lang/IllegalStateException", NULL); 300 return AUDIORECORD_ERROR; 301 } 302 303 return android_media_translateRecorderErrorCode( 304 lpRecorder->start((AudioSystem::sync_event_t)event, triggerSession)); 305} 306 307 308// ---------------------------------------------------------------------------- 309static void 310android_media_AudioRecord_stop(JNIEnv *env, jobject thiz) 311{ 312 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); 313 if (lpRecorder == NULL ) { 314 jniThrowException(env, "java/lang/IllegalStateException", NULL); 315 return; 316 } 317 318 lpRecorder->stop(); 319 //ALOGV("Called lpRecorder->stop()"); 320} 321 322 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} 358 359 360// ---------------------------------------------------------------------------- 361static void android_media_AudioRecord_finalize(JNIEnv *env, jobject thiz) { 362 android_media_AudioRecord_release(env, thiz); 363} 364 365 366// ---------------------------------------------------------------------------- 367static jint android_media_AudioRecord_readInByteArray(JNIEnv *env, jobject thiz, 368 jbyteArray javaAudioData, 369 jint offsetInBytes, jint sizeInBytes) { 370 jbyte* recordBuff = NULL; 371 // get the audio recorder from which we'll read new audio samples 372 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); 373 if (lpRecorder == NULL) { 374 ALOGE("Unable to retrieve AudioRecord object, can't record"); 375 return 0; 376 } 377 378 if (!javaAudioData) { 379 ALOGE("Invalid Java array to store recorded audio, can't record"); 380 return 0; 381 } 382 383 // get the pointer to where we'll record the audio 384 // NOTE: We may use GetPrimitiveArrayCritical() when the JNI implementation changes in such 385 // a way that it becomes much more efficient. When doing so, we will have to prevent the 386 // AudioSystem callback to be called while in critical section (in case of media server 387 // process crash for instance) 388 recordBuff = (jbyte *)env->GetByteArrayElements(javaAudioData, NULL); 389 390 if (recordBuff == NULL) { 391 ALOGE("Error retrieving destination for recorded audio data, can't record"); 392 return 0; 393 } 394 395 // read the new audio data from the native AudioRecord object 396 ssize_t recorderBuffSize = lpRecorder->frameCount()*lpRecorder->frameSize(); 397 ssize_t readSize = lpRecorder->read(recordBuff + offsetInBytes, 398 sizeInBytes > (jint)recorderBuffSize ? 399 (jint)recorderBuffSize : sizeInBytes ); 400 env->ReleaseByteArrayElements(javaAudioData, recordBuff, 0); 401 402 return (jint) readSize; 403} 404 405// ---------------------------------------------------------------------------- 406static jint android_media_AudioRecord_readInShortArray(JNIEnv *env, jobject thiz, 407 jshortArray javaAudioData, 408 jint offsetInShorts, jint sizeInShorts) { 409 410 return (android_media_AudioRecord_readInByteArray(env, thiz, 411 (jbyteArray) javaAudioData, 412 offsetInShorts*2, sizeInShorts*2) 413 / 2); 414} 415 416// ---------------------------------------------------------------------------- 417static jint android_media_AudioRecord_readInDirectBuffer(JNIEnv *env, jobject thiz, 418 jobject jBuffer, jint sizeInBytes) { 419 // get the audio recorder from which we'll read new audio samples 420 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz); 421 if (lpRecorder==NULL) 422 return 0; 423 424 // direct buffer and direct access supported? 425 long capacity = env->GetDirectBufferCapacity(jBuffer); 426 if (capacity == -1) { 427 // buffer direct access is not supported 428 ALOGE("Buffer direct access is not supported, can't record"); 429 return 0; 430 } 431 //ALOGV("capacity = %ld", capacity); 432 jbyte* nativeFromJavaBuf = (jbyte*) env->GetDirectBufferAddress(jBuffer); 433 if (nativeFromJavaBuf==NULL) { 434 ALOGE("Buffer direct access is not supported, can't record"); 435 return 0; 436 } 437 438 // read new data from the recorder 439 return (jint) lpRecorder->read(nativeFromJavaBuf, 440 capacity < sizeInBytes ? capacity : sizeInBytes); 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 int frameCount = 0; 516 status_t result = AudioRecord::getMinFrameCount(&frameCount, 517 sampleRateInHertz, 518 (audioFormat == javaAudioRecordFields.PCM16 ? 519 AUDIO_FORMAT_PCM_16_BIT : AUDIO_FORMAT_PCM_8_BIT), 520 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 == javaAudioRecordFields.PCM16 ? 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_CONST_PCM16_NAME "ENCODING_PCM_16BIT" 561#define JAVA_CONST_PCM8_NAME "ENCODING_PCM_8BIT" 562#define JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME "mNativeRecorderInJavaObj" 563#define JAVA_NATIVECALLBACKINFO_FIELD_NAME "mNativeCallbackCookie" 564 565#define JAVA_AUDIOFORMAT_CLASS_NAME "android/media/AudioFormat" 566 567// ---------------------------------------------------------------------------- 568 569extern bool android_media_getIntConstantFromClass(JNIEnv* pEnv, 570 jclass theClass, const char* className, const char* constName, int* constVal); 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 // Get the format constants from the AudioFormat class 614 jclass audioFormatClass = NULL; 615 audioFormatClass = env->FindClass(JAVA_AUDIOFORMAT_CLASS_NAME); 616 if (audioFormatClass == NULL) { 617 ALOGE("Can't find %s", JAVA_AUDIOFORMAT_CLASS_NAME); 618 return -1; 619 } 620 if ( !android_media_getIntConstantFromClass(env, audioFormatClass, 621 JAVA_AUDIOFORMAT_CLASS_NAME, 622 JAVA_CONST_PCM16_NAME, &(javaAudioRecordFields.PCM16)) 623 || !android_media_getIntConstantFromClass(env, audioFormatClass, 624 JAVA_AUDIOFORMAT_CLASS_NAME, 625 JAVA_CONST_PCM8_NAME, &(javaAudioRecordFields.PCM8)) ) { 626 // error log performed in getIntConstantFromClass() 627 return -1; 628 } 629 630 return AndroidRuntime::registerNativeMethods(env, 631 kClassPathName, gMethods, NELEM(gMethods)); 632} 633 634// ---------------------------------------------------------------------------- 635