• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <inttypes.h>
22 #include <jni.h>
23 #include <JNIHelp.h>
24 #include <android_runtime/AndroidRuntime.h>
25 
26 #include <utils/Log.h>
27 #include <media/AudioRecord.h>
28 
29 #include "android_media_AudioFormat.h"
30 #include "android_media_AudioErrors.h"
31 
32 // ----------------------------------------------------------------------------
33 
34 using namespace android;
35 
36 // ----------------------------------------------------------------------------
37 static const char* const kClassPathName = "android/media/AudioRecord";
38 static const char* const kAudioAttributesClassPathName = "android/media/AudioAttributes";
39 
40 struct audio_record_fields_t {
41     // these fields provide access from C++ to the...
42     jmethodID postNativeEventInJava; //... event post callback method
43     jfieldID  nativeRecorderInJavaObj; // provides access to the C++ AudioRecord object
44     jfieldID  nativeCallbackCookie;    // provides access to the AudioRecord callback data
45 };
46 struct audio_attributes_fields_t {
47     jfieldID  fieldRecSource;    // AudioAttributes.mSource
48     jfieldID  fieldFlags;        // AudioAttributes.mFlags
49     jfieldID  fieldFormattedTags;// AudioAttributes.mFormattedTags
50 };
51 static audio_attributes_fields_t javaAudioAttrFields;
52 static audio_record_fields_t     javaAudioRecordFields;
53 
54 struct audiorecord_callback_cookie {
55     jclass      audioRecord_class;
56     jobject     audioRecord_ref;
57     bool        busy;
58     Condition   cond;
59 };
60 
61 static Mutex sLock;
62 static SortedVector <audiorecord_callback_cookie *> sAudioRecordCallBackCookies;
63 
64 // ----------------------------------------------------------------------------
65 
66 #define AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT      -16
67 #define AUDIORECORD_ERROR_SETUP_INVALIDCHANNELMASK -17
68 #define AUDIORECORD_ERROR_SETUP_INVALIDFORMAT       -18
69 #define AUDIORECORD_ERROR_SETUP_INVALIDSOURCE       -19
70 #define AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED    -20
71 
72 // ----------------------------------------------------------------------------
recorderCallback(int event,void * user,void * info)73 static void recorderCallback(int event, void* user, void *info) {
74 
75     audiorecord_callback_cookie *callbackInfo = (audiorecord_callback_cookie *)user;
76     {
77         Mutex::Autolock l(sLock);
78         if (sAudioRecordCallBackCookies.indexOf(callbackInfo) < 0) {
79             return;
80         }
81         callbackInfo->busy = true;
82     }
83 
84     switch (event) {
85     case AudioRecord::EVENT_MARKER: {
86         JNIEnv *env = AndroidRuntime::getJNIEnv();
87         if (user != NULL && env != NULL) {
88             env->CallStaticVoidMethod(
89                 callbackInfo->audioRecord_class,
90                 javaAudioRecordFields.postNativeEventInJava,
91                 callbackInfo->audioRecord_ref, event, 0,0, NULL);
92             if (env->ExceptionCheck()) {
93                 env->ExceptionDescribe();
94                 env->ExceptionClear();
95             }
96         }
97         } break;
98 
99     case AudioRecord::EVENT_NEW_POS: {
100         JNIEnv *env = AndroidRuntime::getJNIEnv();
101         if (user != NULL && env != NULL) {
102             env->CallStaticVoidMethod(
103                 callbackInfo->audioRecord_class,
104                 javaAudioRecordFields.postNativeEventInJava,
105                 callbackInfo->audioRecord_ref, event, 0,0, NULL);
106             if (env->ExceptionCheck()) {
107                 env->ExceptionDescribe();
108                 env->ExceptionClear();
109             }
110         }
111         } break;
112     }
113 
114     {
115         Mutex::Autolock l(sLock);
116         callbackInfo->busy = false;
117         callbackInfo->cond.broadcast();
118     }
119 }
120 
121 // ----------------------------------------------------------------------------
getAudioRecord(JNIEnv * env,jobject thiz)122 static sp<AudioRecord> getAudioRecord(JNIEnv* env, jobject thiz)
123 {
124     Mutex::Autolock l(sLock);
125     AudioRecord* const ar =
126             (AudioRecord*)env->GetLongField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
127     return sp<AudioRecord>(ar);
128 }
129 
setAudioRecord(JNIEnv * env,jobject thiz,const sp<AudioRecord> & ar)130 static sp<AudioRecord> setAudioRecord(JNIEnv* env, jobject thiz, const sp<AudioRecord>& ar)
131 {
132     Mutex::Autolock l(sLock);
133     sp<AudioRecord> old =
134             (AudioRecord*)env->GetLongField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
135     if (ar.get()) {
136         ar->incStrong((void*)setAudioRecord);
137     }
138     if (old != 0) {
139         old->decStrong((void*)setAudioRecord);
140     }
141     env->SetLongField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj, (jlong)ar.get());
142     return old;
143 }
144 
145 // ----------------------------------------------------------------------------
146 static jint
android_media_AudioRecord_setup(JNIEnv * env,jobject thiz,jobject weak_this,jobject jaa,jint sampleRateInHertz,jint channelMask,jint audioFormat,jint buffSizeInBytes,jintArray jSession)147 android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject weak_this,
148         jobject jaa, jint sampleRateInHertz, jint channelMask,
149                 // Java channel masks map directly to the native definition
150         jint audioFormat, jint buffSizeInBytes, jintArray jSession)
151 {
152     //ALOGV(">> Entering android_media_AudioRecord_setup");
153     //ALOGV("sampleRate=%d, audioFormat=%d, channel mask=%x, buffSizeInBytes=%d",
154     //     sampleRateInHertz, audioFormat, channelMask, buffSizeInBytes);
155 
156     if (jaa == 0) {
157         ALOGE("Error creating AudioRecord: invalid audio attributes");
158         return (jint) AUDIO_JAVA_ERROR;
159     }
160 
161     if (!audio_is_input_channel(channelMask)) {
162         ALOGE("Error creating AudioRecord: channel mask %#x is not valid.", channelMask);
163         return (jint) AUDIORECORD_ERROR_SETUP_INVALIDCHANNELMASK;
164     }
165     uint32_t channelCount = audio_channel_count_from_in_mask(channelMask);
166 
167     // compare the format against the Java constants
168     audio_format_t format = audioFormatToNative(audioFormat);
169     if (format == AUDIO_FORMAT_INVALID) {
170         ALOGE("Error creating AudioRecord: unsupported audio format %d.", audioFormat);
171         return (jint) AUDIORECORD_ERROR_SETUP_INVALIDFORMAT;
172     }
173 
174     size_t bytesPerSample = audio_bytes_per_sample(format);
175 
176     if (buffSizeInBytes == 0) {
177          ALOGE("Error creating AudioRecord: frameCount is 0.");
178         return (jint) AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT;
179     }
180     size_t frameSize = channelCount * bytesPerSample;
181     size_t frameCount = buffSizeInBytes / frameSize;
182 
183     jclass clazz = env->GetObjectClass(thiz);
184     if (clazz == NULL) {
185         ALOGE("Can't find %s when setting up callback.", kClassPathName);
186         return (jint) AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED;
187     }
188 
189     if (jSession == NULL) {
190         ALOGE("Error creating AudioRecord: invalid session ID pointer");
191         return (jint) AUDIO_JAVA_ERROR;
192     }
193 
194     jint* nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
195     if (nSession == NULL) {
196         ALOGE("Error creating AudioRecord: Error retrieving session id pointer");
197         return (jint) AUDIO_JAVA_ERROR;
198     }
199     int sessionId = nSession[0];
200     env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
201     nSession = NULL;
202 
203     // create an uninitialized AudioRecord object
204     sp<AudioRecord> lpRecorder = new AudioRecord();
205 
206     audio_attributes_t *paa = NULL;
207     // read the AudioAttributes values
208     paa = (audio_attributes_t *) calloc(1, sizeof(audio_attributes_t));
209     const jstring jtags =
210             (jstring) env->GetObjectField(jaa, javaAudioAttrFields.fieldFormattedTags);
211     const char* tags = env->GetStringUTFChars(jtags, NULL);
212     // copying array size -1, char array for tags was calloc'd, no need to NULL-terminate it
213     strncpy(paa->tags, tags, AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1);
214     env->ReleaseStringUTFChars(jtags, tags);
215     paa->source = (audio_source_t) env->GetIntField(jaa, javaAudioAttrFields.fieldRecSource);
216     paa->flags = (audio_flags_mask_t)env->GetIntField(jaa, javaAudioAttrFields.fieldFlags);
217     ALOGV("AudioRecord_setup for source=%d tags=%s flags=%08x", paa->source, paa->tags, paa->flags);
218 
219     audio_input_flags_t flags = AUDIO_INPUT_FLAG_NONE;
220     if (paa->flags & AUDIO_FLAG_HW_HOTWORD) {
221         flags = AUDIO_INPUT_FLAG_HW_HOTWORD;
222     }
223     // create the callback information:
224     // this data will be passed with every AudioRecord callback
225     audiorecord_callback_cookie *lpCallbackData = new audiorecord_callback_cookie;
226     lpCallbackData->audioRecord_class = (jclass)env->NewGlobalRef(clazz);
227     // we use a weak reference so the AudioRecord object can be garbage collected.
228     lpCallbackData->audioRecord_ref = env->NewGlobalRef(weak_this);
229     lpCallbackData->busy = false;
230 
231     const status_t status = lpRecorder->set(paa->source,
232         sampleRateInHertz,
233         format,        // word length, PCM
234         channelMask,
235         frameCount,
236         recorderCallback,// callback_t
237         lpCallbackData,// void* user
238         0,             // notificationFrames,
239         true,          // threadCanCallJava
240         sessionId,
241         AudioRecord::TRANSFER_DEFAULT,
242         flags,
243         paa);
244 
245     if (status != NO_ERROR) {
246         ALOGE("Error creating AudioRecord instance: initialization check failed with status %d.",
247                 status);
248         goto native_init_failure;
249     }
250 
251     nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
252     if (nSession == NULL) {
253         ALOGE("Error creating AudioRecord: Error retrieving session id pointer");
254         goto native_init_failure;
255     }
256     // read the audio session ID back from AudioRecord in case a new session was created during set()
257     nSession[0] = lpRecorder->getSessionId();
258     env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
259     nSession = NULL;
260 
261     {   // scope for the lock
262         Mutex::Autolock l(sLock);
263         sAudioRecordCallBackCookies.add(lpCallbackData);
264     }
265     // save our newly created C++ AudioRecord in the "nativeRecorderInJavaObj" field
266     // of the Java object
267     setAudioRecord(env, thiz, lpRecorder);
268 
269     // save our newly created callback information in the "nativeCallbackCookie" field
270     // of the Java object (in mNativeCallbackCookie) so we can free the memory in finalize()
271     env->SetLongField(thiz, javaAudioRecordFields.nativeCallbackCookie, (jlong)lpCallbackData);
272 
273     return (jint) AUDIO_JAVA_SUCCESS;
274 
275     // failure:
276 native_init_failure:
277     env->DeleteGlobalRef(lpCallbackData->audioRecord_class);
278     env->DeleteGlobalRef(lpCallbackData->audioRecord_ref);
279     delete lpCallbackData;
280     env->SetLongField(thiz, javaAudioRecordFields.nativeCallbackCookie, 0);
281 
282     return (jint) AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED;
283 }
284 
285 
286 
287 // ----------------------------------------------------------------------------
288 static jint
android_media_AudioRecord_start(JNIEnv * env,jobject thiz,jint event,jint triggerSession)289 android_media_AudioRecord_start(JNIEnv *env, jobject thiz, jint event, jint triggerSession)
290 {
291     sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
292     if (lpRecorder == NULL ) {
293         jniThrowException(env, "java/lang/IllegalStateException", NULL);
294         return (jint) AUDIO_JAVA_ERROR;
295     }
296 
297     return nativeToJavaStatus(
298             lpRecorder->start((AudioSystem::sync_event_t)event, triggerSession));
299 }
300 
301 
302 // ----------------------------------------------------------------------------
303 static void
android_media_AudioRecord_stop(JNIEnv * env,jobject thiz)304 android_media_AudioRecord_stop(JNIEnv *env, jobject thiz)
305 {
306     sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
307     if (lpRecorder == NULL ) {
308         jniThrowException(env, "java/lang/IllegalStateException", NULL);
309         return;
310     }
311 
312     lpRecorder->stop();
313     //ALOGV("Called lpRecorder->stop()");
314 }
315 
316 
317 // ----------------------------------------------------------------------------
318 
319 #define CALLBACK_COND_WAIT_TIMEOUT_MS 1000
android_media_AudioRecord_release(JNIEnv * env,jobject thiz)320 static void android_media_AudioRecord_release(JNIEnv *env,  jobject thiz) {
321     sp<AudioRecord> lpRecorder = setAudioRecord(env, thiz, 0);
322     if (lpRecorder == NULL) {
323         return;
324     }
325     ALOGV("About to delete lpRecorder: %p", lpRecorder.get());
326     lpRecorder->stop();
327 
328     audiorecord_callback_cookie *lpCookie = (audiorecord_callback_cookie *)env->GetLongField(
329         thiz, javaAudioRecordFields.nativeCallbackCookie);
330 
331     // reset the native resources in the Java object so any attempt to access
332     // them after a call to release fails.
333     env->SetLongField(thiz, javaAudioRecordFields.nativeCallbackCookie, 0);
334 
335     // delete the callback information
336     if (lpCookie) {
337         Mutex::Autolock l(sLock);
338         ALOGV("deleting lpCookie: %p", lpCookie);
339         while (lpCookie->busy) {
340             if (lpCookie->cond.waitRelative(sLock,
341                                             milliseconds(CALLBACK_COND_WAIT_TIMEOUT_MS)) !=
342                                                     NO_ERROR) {
343                 break;
344             }
345         }
346         sAudioRecordCallBackCookies.remove(lpCookie);
347         env->DeleteGlobalRef(lpCookie->audioRecord_class);
348         env->DeleteGlobalRef(lpCookie->audioRecord_ref);
349         delete lpCookie;
350     }
351 }
352 
353 
354 // ----------------------------------------------------------------------------
android_media_AudioRecord_finalize(JNIEnv * env,jobject thiz)355 static void android_media_AudioRecord_finalize(JNIEnv *env,  jobject thiz) {
356     android_media_AudioRecord_release(env, thiz);
357 }
358 
359 
360 // ----------------------------------------------------------------------------
android_media_AudioRecord_readInByteArray(JNIEnv * env,jobject thiz,jbyteArray javaAudioData,jint offsetInBytes,jint sizeInBytes)361 static jint android_media_AudioRecord_readInByteArray(JNIEnv *env,  jobject thiz,
362                                                         jbyteArray javaAudioData,
363                                                         jint offsetInBytes, jint sizeInBytes) {
364     jbyte* recordBuff = NULL;
365     // get the audio recorder from which we'll read new audio samples
366     sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
367     if (lpRecorder == NULL) {
368         ALOGE("Unable to retrieve AudioRecord object, can't record");
369         return 0;
370     }
371 
372     if (!javaAudioData) {
373         ALOGE("Invalid Java array to store recorded audio, can't record");
374         return 0;
375     }
376 
377     // get the pointer to where we'll record the audio
378     // NOTE: We may use GetPrimitiveArrayCritical() when the JNI implementation changes in such
379     // a way that it becomes much more efficient. When doing so, we will have to prevent the
380     // AudioSystem callback to be called while in critical section (in case of media server
381     // process crash for instance)
382     recordBuff = (jbyte *)env->GetByteArrayElements(javaAudioData, NULL);
383 
384     if (recordBuff == NULL) {
385         ALOGE("Error retrieving destination for recorded audio data, can't record");
386         return 0;
387     }
388 
389     // read the new audio data from the native AudioRecord object
390     ssize_t recorderBuffSize = lpRecorder->frameCount()*lpRecorder->frameSize();
391     ssize_t readSize = lpRecorder->read(recordBuff + offsetInBytes,
392                                         sizeInBytes > (jint)recorderBuffSize ?
393                                             (jint)recorderBuffSize : sizeInBytes );
394     env->ReleaseByteArrayElements(javaAudioData, recordBuff, 0);
395 
396     if (readSize < 0) {
397         readSize = (jint)AUDIO_JAVA_INVALID_OPERATION;
398     }
399     return (jint) readSize;
400 }
401 
402 // ----------------------------------------------------------------------------
android_media_AudioRecord_readInShortArray(JNIEnv * env,jobject thiz,jshortArray javaAudioData,jint offsetInShorts,jint sizeInShorts)403 static jint android_media_AudioRecord_readInShortArray(JNIEnv *env,  jobject thiz,
404                                                         jshortArray javaAudioData,
405                                                         jint offsetInShorts, jint sizeInShorts) {
406 
407     jshort* recordBuff = NULL;
408     // get the audio recorder from which we'll read new audio samples
409     sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
410     if (lpRecorder == NULL) {
411         ALOGE("Unable to retrieve AudioRecord object, can't record");
412         return 0;
413     }
414 
415     if (!javaAudioData) {
416         ALOGE("Invalid Java array to store recorded audio, can't record");
417         return 0;
418     }
419 
420     // get the pointer to where we'll record the audio
421     // NOTE: We may use GetPrimitiveArrayCritical() when the JNI implementation changes in such
422     // a way that it becomes much more efficient. When doing so, we will have to prevent the
423     // AudioSystem callback to be called while in critical section (in case of media server
424     // process crash for instance)
425     recordBuff = (jshort *)env->GetShortArrayElements(javaAudioData, NULL);
426 
427     if (recordBuff == NULL) {
428         ALOGE("Error retrieving destination for recorded audio data, can't record");
429         return 0;
430     }
431 
432     // read the new audio data from the native AudioRecord object
433     const size_t recorderBuffSize = lpRecorder->frameCount()*lpRecorder->frameSize();
434     const size_t sizeInBytes = sizeInShorts * sizeof(short);
435     ssize_t readSize = lpRecorder->read(recordBuff + offsetInShorts,
436                                         sizeInBytes > recorderBuffSize ?
437                                             recorderBuffSize : sizeInBytes);
438 
439     env->ReleaseShortArrayElements(javaAudioData, recordBuff, 0);
440 
441     if (readSize < 0) {
442         readSize = (jint)AUDIO_JAVA_INVALID_OPERATION;
443     } else {
444         readSize /= sizeof(short);
445     }
446     return (jint) readSize;
447 }
448 
449 // ----------------------------------------------------------------------------
android_media_AudioRecord_readInDirectBuffer(JNIEnv * env,jobject thiz,jobject jBuffer,jint sizeInBytes)450 static jint android_media_AudioRecord_readInDirectBuffer(JNIEnv *env,  jobject thiz,
451                                                   jobject jBuffer, jint sizeInBytes) {
452     // get the audio recorder from which we'll read new audio samples
453     sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
454     if (lpRecorder==NULL)
455         return 0;
456 
457     // direct buffer and direct access supported?
458     long capacity = env->GetDirectBufferCapacity(jBuffer);
459     if (capacity == -1) {
460         // buffer direct access is not supported
461         ALOGE("Buffer direct access is not supported, can't record");
462         return 0;
463     }
464     //ALOGV("capacity = %ld", capacity);
465     jbyte* nativeFromJavaBuf = (jbyte*) env->GetDirectBufferAddress(jBuffer);
466     if (nativeFromJavaBuf==NULL) {
467         ALOGE("Buffer direct access is not supported, can't record");
468         return 0;
469     }
470 
471     // read new data from the recorder
472     ssize_t readSize = lpRecorder->read(nativeFromJavaBuf,
473                                    capacity < sizeInBytes ? capacity : sizeInBytes);
474     if (readSize < 0) {
475         readSize = (jint)AUDIO_JAVA_INVALID_OPERATION;
476     }
477     return (jint)readSize;
478 }
479 
480 
481 // ----------------------------------------------------------------------------
android_media_AudioRecord_set_marker_pos(JNIEnv * env,jobject thiz,jint markerPos)482 static jint android_media_AudioRecord_set_marker_pos(JNIEnv *env,  jobject thiz,
483         jint markerPos) {
484 
485     sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
486     if (lpRecorder == NULL) {
487         jniThrowException(env, "java/lang/IllegalStateException",
488             "Unable to retrieve AudioRecord pointer for setMarkerPosition()");
489         return (jint)AUDIO_JAVA_ERROR;
490     }
491     return nativeToJavaStatus( lpRecorder->setMarkerPosition(markerPos) );
492 }
493 
494 
495 // ----------------------------------------------------------------------------
android_media_AudioRecord_get_marker_pos(JNIEnv * env,jobject thiz)496 static jint android_media_AudioRecord_get_marker_pos(JNIEnv *env,  jobject thiz) {
497 
498     sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
499     uint32_t markerPos = 0;
500 
501     if (lpRecorder == NULL) {
502         jniThrowException(env, "java/lang/IllegalStateException",
503             "Unable to retrieve AudioRecord pointer for getMarkerPosition()");
504         return (jint)AUDIO_JAVA_ERROR;
505     }
506     lpRecorder->getMarkerPosition(&markerPos);
507     return (jint)markerPos;
508 }
509 
510 
511 // ----------------------------------------------------------------------------
android_media_AudioRecord_set_pos_update_period(JNIEnv * env,jobject thiz,jint period)512 static jint android_media_AudioRecord_set_pos_update_period(JNIEnv *env,  jobject thiz,
513         jint period) {
514 
515     sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
516 
517     if (lpRecorder == NULL) {
518         jniThrowException(env, "java/lang/IllegalStateException",
519             "Unable to retrieve AudioRecord pointer for setPositionUpdatePeriod()");
520         return (jint)AUDIO_JAVA_ERROR;
521     }
522     return nativeToJavaStatus( lpRecorder->setPositionUpdatePeriod(period) );
523 }
524 
525 
526 // ----------------------------------------------------------------------------
android_media_AudioRecord_get_pos_update_period(JNIEnv * env,jobject thiz)527 static jint android_media_AudioRecord_get_pos_update_period(JNIEnv *env,  jobject thiz) {
528 
529     sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
530     uint32_t period = 0;
531 
532     if (lpRecorder == NULL) {
533         jniThrowException(env, "java/lang/IllegalStateException",
534             "Unable to retrieve AudioRecord pointer for getPositionUpdatePeriod()");
535         return (jint)AUDIO_JAVA_ERROR;
536     }
537     lpRecorder->getPositionUpdatePeriod(&period);
538     return (jint)period;
539 }
540 
541 
542 // ----------------------------------------------------------------------------
543 // returns the minimum required size for the successful creation of an AudioRecord instance.
544 // returns 0 if the parameter combination is not supported.
545 // return -1 if there was an error querying the buffer size.
android_media_AudioRecord_get_min_buff_size(JNIEnv * env,jobject thiz,jint sampleRateInHertz,jint channelCount,jint audioFormat)546 static jint android_media_AudioRecord_get_min_buff_size(JNIEnv *env,  jobject thiz,
547     jint sampleRateInHertz, jint channelCount, jint audioFormat) {
548 
549     ALOGV(">> android_media_AudioRecord_get_min_buff_size(%d, %d, %d)",
550           sampleRateInHertz, channelCount, audioFormat);
551 
552     size_t frameCount = 0;
553     audio_format_t format = audioFormatToNative(audioFormat);
554     status_t result = AudioRecord::getMinFrameCount(&frameCount,
555             sampleRateInHertz,
556             format,
557             audio_channel_in_mask_from_count(channelCount));
558 
559     if (result == BAD_VALUE) {
560         return 0;
561     }
562     if (result != NO_ERROR) {
563         return -1;
564     }
565     return frameCount * channelCount * audio_bytes_per_sample(format);
566 }
567 
568 
569 // ----------------------------------------------------------------------------
570 // ----------------------------------------------------------------------------
571 static JNINativeMethod gMethods[] = {
572     // name,               signature,  funcPtr
573     {"native_start",         "(II)I",    (void *)android_media_AudioRecord_start},
574     {"native_stop",          "()V",    (void *)android_media_AudioRecord_stop},
575     {"native_setup",         "(Ljava/lang/Object;Ljava/lang/Object;IIII[I)I",
576                                        (void *)android_media_AudioRecord_setup},
577     {"native_finalize",      "()V",    (void *)android_media_AudioRecord_finalize},
578     {"native_release",       "()V",    (void *)android_media_AudioRecord_release},
579     {"native_read_in_byte_array",
580                              "([BII)I", (void *)android_media_AudioRecord_readInByteArray},
581     {"native_read_in_short_array",
582                              "([SII)I", (void *)android_media_AudioRecord_readInShortArray},
583     {"native_read_in_direct_buffer","(Ljava/lang/Object;I)I",
584                                        (void *)android_media_AudioRecord_readInDirectBuffer},
585     {"native_set_marker_pos","(I)I",   (void *)android_media_AudioRecord_set_marker_pos},
586     {"native_get_marker_pos","()I",    (void *)android_media_AudioRecord_get_marker_pos},
587     {"native_set_pos_update_period",
588                              "(I)I",   (void *)android_media_AudioRecord_set_pos_update_period},
589     {"native_get_pos_update_period",
590                              "()I",    (void *)android_media_AudioRecord_get_pos_update_period},
591     {"native_get_min_buff_size",
592                              "(III)I",   (void *)android_media_AudioRecord_get_min_buff_size},
593 };
594 
595 // field names found in android/media/AudioRecord.java
596 #define JAVA_POSTEVENT_CALLBACK_NAME  "postEventFromNative"
597 #define JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME  "mNativeRecorderInJavaObj"
598 #define JAVA_NATIVECALLBACKINFO_FIELD_NAME       "mNativeCallbackCookie"
599 
600 // ----------------------------------------------------------------------------
register_android_media_AudioRecord(JNIEnv * env)601 int register_android_media_AudioRecord(JNIEnv *env)
602 {
603     javaAudioRecordFields.postNativeEventInJava = NULL;
604     javaAudioRecordFields.nativeRecorderInJavaObj = NULL;
605     javaAudioRecordFields.nativeCallbackCookie = NULL;
606 
607 
608     // Get the AudioRecord class
609     jclass audioRecordClass = env->FindClass(kClassPathName);
610     if (audioRecordClass == NULL) {
611         ALOGE("Can't find %s", kClassPathName);
612         return -1;
613     }
614     // Get the postEvent method
615     javaAudioRecordFields.postNativeEventInJava = env->GetStaticMethodID(
616             audioRecordClass,
617             JAVA_POSTEVENT_CALLBACK_NAME, "(Ljava/lang/Object;IIILjava/lang/Object;)V");
618     if (javaAudioRecordFields.postNativeEventInJava == NULL) {
619         ALOGE("Can't find AudioRecord.%s", JAVA_POSTEVENT_CALLBACK_NAME);
620         return -1;
621     }
622 
623     // Get the variables
624     //    mNativeRecorderInJavaObj
625     javaAudioRecordFields.nativeRecorderInJavaObj =
626         env->GetFieldID(audioRecordClass,
627                         JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME, "J");
628     if (javaAudioRecordFields.nativeRecorderInJavaObj == NULL) {
629         ALOGE("Can't find AudioRecord.%s", JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME);
630         return -1;
631     }
632     //     mNativeCallbackCookie
633     javaAudioRecordFields.nativeCallbackCookie = env->GetFieldID(
634             audioRecordClass,
635             JAVA_NATIVECALLBACKINFO_FIELD_NAME, "J");
636     if (javaAudioRecordFields.nativeCallbackCookie == NULL) {
637         ALOGE("Can't find AudioRecord.%s", JAVA_NATIVECALLBACKINFO_FIELD_NAME);
638         return -1;
639     }
640 
641     // Get the AudioAttributes class and fields
642     jclass audioAttrClass = env->FindClass(kAudioAttributesClassPathName);
643     if (audioAttrClass == NULL) {
644         ALOGE("Can't find %s", kAudioAttributesClassPathName);
645         return -1;
646     }
647     jclass audioAttributesClassRef = (jclass)env->NewGlobalRef(audioAttrClass);
648     javaAudioAttrFields.fieldRecSource = env->GetFieldID(audioAttributesClassRef, "mSource", "I");
649     javaAudioAttrFields.fieldFlags = env->GetFieldID(audioAttributesClassRef, "mFlags", "I");
650     javaAudioAttrFields.fieldFormattedTags =
651             env->GetFieldID(audioAttributesClassRef, "mFormattedTags", "Ljava/lang/String;");
652     env->DeleteGlobalRef(audioAttributesClassRef);
653     if (javaAudioAttrFields.fieldRecSource == NULL
654             || javaAudioAttrFields.fieldFlags == NULL
655             || javaAudioAttrFields.fieldFormattedTags == NULL) {
656         ALOGE("Can't initialize AudioAttributes fields");
657         return -1;
658     }
659 
660     return AndroidRuntime::registerNativeMethods(env,
661             kClassPathName, gMethods, NELEM(gMethods));
662 }
663 
664 // ----------------------------------------------------------------------------
665