blob: 33db4a85f2894de2ea4d1a50c6329ff14c50a09e [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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
Kévin PETIT95ece352014-02-13 11:02:27 +000021#include <inttypes.h>
Glenn Kastenc81d31c2012-03-13 14:46:23 -070022#include <jni.h>
23#include <JNIHelp.h>
Andreas Gampeed6b9df2014-11-20 22:02:20 -080024#include "core_jni_helpers.h"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080025
Glenn Kastenc81d31c2012-03-13 14:46:23 -070026#include <utils/Log.h>
27#include <media/AudioRecord.h>
Dima Zavin24fc2fb2011-04-19 22:30:36 -070028
Glenn Kastenfe834d32014-01-08 14:49:08 -080029#include "android_media_AudioFormat.h"
Eric Laurentbc11a692014-05-16 12:19:25 -070030#include "android_media_AudioErrors.h"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080031
32// ----------------------------------------------------------------------------
33
34using namespace android;
35
36// ----------------------------------------------------------------------------
37static const char* const kClassPathName = "android/media/AudioRecord";
Jean-Michel Trivi701d6ff2014-07-16 07:51:22 -070038static const char* const kAudioAttributesClassPathName = "android/media/AudioAttributes";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080039
Jean-Michel Trivi701d6ff2014-07-16 07:51:22 -070040struct audio_record_fields_t {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080041 // these fields provide access from C++ to the...
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080042 jmethodID postNativeEventInJava; //... event post callback method
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080043 jfieldID nativeRecorderInJavaObj; // provides access to the C++ AudioRecord object
44 jfieldID nativeCallbackCookie; // provides access to the AudioRecord callback data
45};
Jean-Michel Trivi701d6ff2014-07-16 07:51:22 -070046struct audio_attributes_fields_t {
47 jfieldID fieldRecSource; // AudioAttributes.mSource
Eric Laurentbdad1af2014-09-19 17:43:29 -070048 jfieldID fieldFlags; // AudioAttributes.mFlags
Jean-Michel Trivi701d6ff2014-07-16 07:51:22 -070049 jfieldID fieldFormattedTags;// AudioAttributes.mFormattedTags
50};
51static audio_attributes_fields_t javaAudioAttrFields;
52static audio_record_fields_t javaAudioRecordFields;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080053
54struct audiorecord_callback_cookie {
55 jclass audioRecord_class;
56 jobject audioRecord_ref;
Eric Laurent532bc1c2012-04-20 12:45:03 -070057 bool busy;
58 Condition cond;
59};
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080060
Eric Laurent532bc1c2012-04-20 12:45:03 -070061static Mutex sLock;
62static SortedVector <audiorecord_callback_cookie *> sAudioRecordCallBackCookies;
Dave Sparkse6335c92010-03-13 17:08:22 -080063
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080064// ----------------------------------------------------------------------------
65
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080066#define AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT -16
Eric Laurenta553c252009-07-17 12:17:14 -070067#define AUDIORECORD_ERROR_SETUP_INVALIDCHANNELMASK -17
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080068#define AUDIORECORD_ERROR_SETUP_INVALIDFORMAT -18
Eric Laurent4bc035a2009-05-22 09:18:15 -070069#define AUDIORECORD_ERROR_SETUP_INVALIDSOURCE -19
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080070#define AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED -20
71
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080072// ----------------------------------------------------------------------------
73static void recorderCallback(int event, void* user, void *info) {
Eric Laurent532bc1c2012-04-20 12:45:03 -070074
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 }
Glenn Kasten18db49a2012-03-12 16:29:55 -070083
Glenn Kasten5b1576c2013-07-18 16:58:19 -070084 switch (event) {
85 case AudioRecord::EVENT_MARKER: {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080086 JNIEnv *env = AndroidRuntime::getJNIEnv();
Glenn Kastena667ff32013-07-22 07:36:34 -070087 if (user != NULL && env != NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080088 env->CallStaticVoidMethod(
Glenn Kasten18db49a2012-03-12 16:29:55 -070089 callbackInfo->audioRecord_class,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080090 javaAudioRecordFields.postNativeEventInJava,
91 callbackInfo->audioRecord_ref, event, 0,0, NULL);
92 if (env->ExceptionCheck()) {
93 env->ExceptionDescribe();
94 env->ExceptionClear();
95 }
96 }
Glenn Kasten5b1576c2013-07-18 16:58:19 -070097 } break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080098
Glenn Kasten5b1576c2013-07-18 16:58:19 -070099 case AudioRecord::EVENT_NEW_POS: {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800100 JNIEnv *env = AndroidRuntime::getJNIEnv();
Glenn Kastena667ff32013-07-22 07:36:34 -0700101 if (user != NULL && env != NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800102 env->CallStaticVoidMethod(
Glenn Kasten18db49a2012-03-12 16:29:55 -0700103 callbackInfo->audioRecord_class,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800104 javaAudioRecordFields.postNativeEventInJava,
105 callbackInfo->audioRecord_ref, event, 0,0, NULL);
106 if (env->ExceptionCheck()) {
107 env->ExceptionDescribe();
108 env->ExceptionClear();
109 }
110 }
Glenn Kasten5b1576c2013-07-18 16:58:19 -0700111 } break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800112 }
Glenn Kasten5b1576c2013-07-18 16:58:19 -0700113
Eric Laurent532bc1c2012-04-20 12:45:03 -0700114 {
115 Mutex::Autolock l(sLock);
116 callbackInfo->busy = false;
117 callbackInfo->cond.broadcast();
118 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800119}
120
Eric Laurent532bc1c2012-04-20 12:45:03 -0700121// ----------------------------------------------------------------------------
122static sp<AudioRecord> getAudioRecord(JNIEnv* env, jobject thiz)
123{
124 Mutex::Autolock l(sLock);
125 AudioRecord* const ar =
Ashok Bhat075e9a12014-01-06 13:45:09 +0000126 (AudioRecord*)env->GetLongField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
Eric Laurent532bc1c2012-04-20 12:45:03 -0700127 return sp<AudioRecord>(ar);
128}
129
130static sp<AudioRecord> setAudioRecord(JNIEnv* env, jobject thiz, const sp<AudioRecord>& ar)
131{
132 Mutex::Autolock l(sLock);
133 sp<AudioRecord> old =
Ashok Bhat075e9a12014-01-06 13:45:09 +0000134 (AudioRecord*)env->GetLongField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj);
Eric Laurent532bc1c2012-04-20 12:45:03 -0700135 if (ar.get()) {
Mathias Agopianb1d90c82013-03-06 17:45:42 -0800136 ar->incStrong((void*)setAudioRecord);
Eric Laurent532bc1c2012-04-20 12:45:03 -0700137 }
138 if (old != 0) {
Mathias Agopianb1d90c82013-03-06 17:45:42 -0800139 old->decStrong((void*)setAudioRecord);
Eric Laurent532bc1c2012-04-20 12:45:03 -0700140 }
Ashok Bhat075e9a12014-01-06 13:45:09 +0000141 env->SetLongField(thiz, javaAudioRecordFields.nativeRecorderInJavaObj, (jlong)ar.get());
Eric Laurent532bc1c2012-04-20 12:45:03 -0700142 return old;
143}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800144
145// ----------------------------------------------------------------------------
Ashok Bhat075e9a12014-01-06 13:45:09 +0000146static jint
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800147android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject weak_this,
Jean-Michel Trivi701d6ff2014-07-16 07:51:22 -0700148 jobject jaa, jint sampleRateInHertz, jint channelMask,
Glenn Kasten33c437d2013-07-18 17:04:21 -0700149 // Java channel masks map directly to the native definition
Eric Laurent44ff4cd2011-06-18 10:34:05 -0700150 jint audioFormat, jint buffSizeInBytes, jintArray jSession)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800151{
Steve Block71f2cf12011-10-20 11:56:00 +0100152 //ALOGV(">> Entering android_media_AudioRecord_setup");
Glenn Kasten33c437d2013-07-18 17:04:21 -0700153 //ALOGV("sampleRate=%d, audioFormat=%d, channel mask=%x, buffSizeInBytes=%d",
154 // sampleRateInHertz, audioFormat, channelMask, buffSizeInBytes);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800155
Jean-Michel Trivi701d6ff2014-07-16 07:51:22 -0700156 if (jaa == 0) {
157 ALOGE("Error creating AudioRecord: invalid audio attributes");
158 return (jint) AUDIO_JAVA_ERROR;
159 }
160
Glenn Kasten33c437d2013-07-18 17:04:21 -0700161 if (!audio_is_input_channel(channelMask)) {
162 ALOGE("Error creating AudioRecord: channel mask %#x is not valid.", channelMask);
Ashok Bhat075e9a12014-01-06 13:45:09 +0000163 return (jint) AUDIORECORD_ERROR_SETUP_INVALIDCHANNELMASK;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800164 }
Eric Laurentdbf55662014-07-01 18:56:30 -0700165 uint32_t channelCount = audio_channel_count_from_in_mask(channelMask);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800166
167 // compare the format against the Java constants
Glenn Kastena5a42382013-12-19 12:34:56 -0800168 audio_format_t format = audioFormatToNative(audioFormat);
169 if (format == AUDIO_FORMAT_INVALID) {
Glenn Kastena9838af2013-11-22 13:34:35 -0800170 ALOGE("Error creating AudioRecord: unsupported audio format %d.", audioFormat);
Ashok Bhat075e9a12014-01-06 13:45:09 +0000171 return (jint) AUDIORECORD_ERROR_SETUP_INVALIDFORMAT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800172 }
173
Glenn Kastena5a42382013-12-19 12:34:56 -0800174 size_t bytesPerSample = audio_bytes_per_sample(format);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800175
176 if (buffSizeInBytes == 0) {
Steve Block3762c312012-01-06 19:20:56 +0000177 ALOGE("Error creating AudioRecord: frameCount is 0.");
Ashok Bhat075e9a12014-01-06 13:45:09 +0000178 return (jint) AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800179 }
Glenn Kasten5b8fd442013-11-14 09:44:14 -0800180 size_t frameSize = channelCount * bytesPerSample;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800181 size_t frameCount = buffSizeInBytes / frameSize;
Glenn Kasten18db49a2012-03-12 16:29:55 -0700182
Eric Laurent532bc1c2012-04-20 12:45:03 -0700183 jclass clazz = env->GetObjectClass(thiz);
184 if (clazz == NULL) {
185 ALOGE("Can't find %s when setting up callback.", kClassPathName);
Ashok Bhat075e9a12014-01-06 13:45:09 +0000186 return (jint) AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED;
Eric Laurent532bc1c2012-04-20 12:45:03 -0700187 }
188
Eric Laurent44ff4cd2011-06-18 10:34:05 -0700189 if (jSession == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000190 ALOGE("Error creating AudioRecord: invalid session ID pointer");
Eric Laurentbc11a692014-05-16 12:19:25 -0700191 return (jint) AUDIO_JAVA_ERROR;
Eric Laurent44ff4cd2011-06-18 10:34:05 -0700192 }
193
194 jint* nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
195 if (nSession == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000196 ALOGE("Error creating AudioRecord: Error retrieving session id pointer");
Eric Laurentbc11a692014-05-16 12:19:25 -0700197 return (jint) AUDIO_JAVA_ERROR;
Eric Laurent44ff4cd2011-06-18 10:34:05 -0700198 }
199 int sessionId = nSession[0];
200 env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
201 nSession = NULL;
202
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800203 // create an uninitialized AudioRecord object
Eric Laurent532bc1c2012-04-20 12:45:03 -0700204 sp<AudioRecord> lpRecorder = new AudioRecord();
Glenn Kasten18db49a2012-03-12 16:29:55 -0700205
Jean-Michel Trivi701d6ff2014-07-16 07:51:22 -0700206 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);
Eric Laurentbdad1af2014-09-19 17:43:29 -0700216 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);
Jean-Michel Trivi701d6ff2014-07-16 07:51:22 -0700218
Eric Laurentbdad1af2014-09-19 17:43:29 -0700219 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 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800223 // create the callback information:
224 // this data will be passed with every AudioRecord callback
Eric Laurent532bc1c2012-04-20 12:45:03 -0700225 audiorecord_callback_cookie *lpCallbackData = new audiorecord_callback_cookie;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800226 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);
Eric Laurent532bc1c2012-04-20 12:45:03 -0700229 lpCallbackData->busy = false;
Glenn Kasten18db49a2012-03-12 16:29:55 -0700230
Jean-Michel Trivi701d6ff2014-07-16 07:51:22 -0700231 const status_t status = lpRecorder->set(paa->source,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800232 sampleRateInHertz,
233 format, // word length, PCM
Glenn Kasten33c437d2013-07-18 17:04:21 -0700234 channelMask,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800235 frameCount,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800236 recorderCallback,// callback_t
237 lpCallbackData,// void* user
238 0, // notificationFrames,
Glenn Kasten86fad472012-06-21 16:21:36 -0700239 true, // threadCanCallJava
Eric Laurentbdad1af2014-09-19 17:43:29 -0700240 sessionId,
241 AudioRecord::TRANSFER_DEFAULT,
Eric Laurent4c3fc59b2014-11-26 09:46:44 -0800242 flags,
243 paa);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800244
Glenn Kastena9838af2013-11-22 13:34:35 -0800245 if (status != NO_ERROR) {
246 ALOGE("Error creating AudioRecord instance: initialization check failed with status %d.",
247 status);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800248 goto native_init_failure;
249 }
250
Eric Laurent44ff4cd2011-06-18 10:34:05 -0700251 nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
252 if (nSession == NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000253 ALOGE("Error creating AudioRecord: Error retrieving session id pointer");
Eric Laurent44ff4cd2011-06-18 10:34:05 -0700254 goto native_init_failure;
255 }
Glenn Kastenb3db2132012-01-19 08:59:58 -0800256 // read the audio session ID back from AudioRecord in case a new session was created during set()
Eric Laurent44ff4cd2011-06-18 10:34:05 -0700257 nSession[0] = lpRecorder->getSessionId();
258 env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
259 nSession = NULL;
260
Eric Laurent532bc1c2012-04-20 12:45:03 -0700261 { // scope for the lock
262 Mutex::Autolock l(sLock);
263 sAudioRecordCallBackCookies.add(lpCallbackData);
264 }
Glenn Kasten18db49a2012-03-12 16:29:55 -0700265 // save our newly created C++ AudioRecord in the "nativeRecorderInJavaObj" field
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800266 // of the Java object
Eric Laurent532bc1c2012-04-20 12:45:03 -0700267 setAudioRecord(env, thiz, lpRecorder);
Glenn Kasten18db49a2012-03-12 16:29:55 -0700268
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800269 // 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()
Ashok Bhat075e9a12014-01-06 13:45:09 +0000271 env->SetLongField(thiz, javaAudioRecordFields.nativeCallbackCookie, (jlong)lpCallbackData);
Glenn Kasten18db49a2012-03-12 16:29:55 -0700272
Eric Laurentbc11a692014-05-16 12:19:25 -0700273 return (jint) AUDIO_JAVA_SUCCESS;
Glenn Kasten18db49a2012-03-12 16:29:55 -0700274
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800275 // failure:
276native_init_failure:
Jean-Michel Trivi4bac5a32009-07-17 12:05:31 -0700277 env->DeleteGlobalRef(lpCallbackData->audioRecord_class);
278 env->DeleteGlobalRef(lpCallbackData->audioRecord_ref);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800279 delete lpCallbackData;
Ashok Bhat075e9a12014-01-06 13:45:09 +0000280 env->SetLongField(thiz, javaAudioRecordFields.nativeCallbackCookie, 0);
Glenn Kasten18db49a2012-03-12 16:29:55 -0700281
Ashok Bhat075e9a12014-01-06 13:45:09 +0000282 return (jint) AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800283}
284
285
286
287// ----------------------------------------------------------------------------
Ashok Bhat075e9a12014-01-06 13:45:09 +0000288static jint
Eric Laurent505e5c82012-03-29 15:19:36 -0700289android_media_AudioRecord_start(JNIEnv *env, jobject thiz, jint event, jint triggerSession)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800290{
Eric Laurent532bc1c2012-04-20 12:45:03 -0700291 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800292 if (lpRecorder == NULL ) {
293 jniThrowException(env, "java/lang/IllegalStateException", NULL);
Eric Laurentbc11a692014-05-16 12:19:25 -0700294 return (jint) AUDIO_JAVA_ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800295 }
Glenn Kasten18db49a2012-03-12 16:29:55 -0700296
Eric Laurentbc11a692014-05-16 12:19:25 -0700297 return nativeToJavaStatus(
Eric Laurent505e5c82012-03-29 15:19:36 -0700298 lpRecorder->start((AudioSystem::sync_event_t)event, triggerSession));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800299}
300
301
302// ----------------------------------------------------------------------------
303static void
304android_media_AudioRecord_stop(JNIEnv *env, jobject thiz)
305{
Eric Laurent532bc1c2012-04-20 12:45:03 -0700306 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800307 if (lpRecorder == NULL ) {
308 jniThrowException(env, "java/lang/IllegalStateException", NULL);
309 return;
310 }
311
312 lpRecorder->stop();
Steve Block71f2cf12011-10-20 11:56:00 +0100313 //ALOGV("Called lpRecorder->stop()");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800314}
315
316
317// ----------------------------------------------------------------------------
Dave Sparkse6335c92010-03-13 17:08:22 -0800318
Eric Laurent532bc1c2012-04-20 12:45:03 -0700319#define CALLBACK_COND_WAIT_TIMEOUT_MS 1000
320static void android_media_AudioRecord_release(JNIEnv *env, jobject thiz) {
321 sp<AudioRecord> lpRecorder = setAudioRecord(env, thiz, 0);
322 if (lpRecorder == NULL) {
323 return;
324 }
Glenn Kasten2fbf25b2014-03-28 15:41:58 -0700325 ALOGV("About to delete lpRecorder: %p", lpRecorder.get());
Eric Laurent532bc1c2012-04-20 12:45:03 -0700326 lpRecorder->stop();
327
Ashok Bhat075e9a12014-01-06 13:45:09 +0000328 audiorecord_callback_cookie *lpCookie = (audiorecord_callback_cookie *)env->GetLongField(
Dave Sparkse6335c92010-03-13 17:08:22 -0800329 thiz, javaAudioRecordFields.nativeCallbackCookie);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800330
Dave Sparkse6335c92010-03-13 17:08:22 -0800331 // reset the native resources in the Java object so any attempt to access
332 // them after a call to release fails.
Ashok Bhat075e9a12014-01-06 13:45:09 +0000333 env->SetLongField(thiz, javaAudioRecordFields.nativeCallbackCookie, 0);
Dave Sparkse6335c92010-03-13 17:08:22 -0800334
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800335 // delete the callback information
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800336 if (lpCookie) {
Eric Laurent532bc1c2012-04-20 12:45:03 -0700337 Mutex::Autolock l(sLock);
Glenn Kasten2fbf25b2014-03-28 15:41:58 -0700338 ALOGV("deleting lpCookie: %p", lpCookie);
Eric Laurent532bc1c2012-04-20 12:45:03 -0700339 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);
Jean-Michel Trivi4bac5a32009-07-17 12:05:31 -0700347 env->DeleteGlobalRef(lpCookie->audioRecord_class);
348 env->DeleteGlobalRef(lpCookie->audioRecord_ref);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800349 delete lpCookie;
350 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800351}
352
353
354// ----------------------------------------------------------------------------
Dave Sparkse6335c92010-03-13 17:08:22 -0800355static void android_media_AudioRecord_finalize(JNIEnv *env, jobject thiz) {
356 android_media_AudioRecord_release(env, thiz);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800357}
358
Andy Hung0e9e2f32015-04-13 23:47:18 -0700359// overloaded JNI array helper functions
360static inline
361jbyte *envGetArrayElements(JNIEnv *env, jbyteArray array, jboolean *isCopy) {
362 return env->GetByteArrayElements(array, isCopy);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800363}
364
Andy Hung0e9e2f32015-04-13 23:47:18 -0700365static inline
366void envReleaseArrayElements(JNIEnv *env, jbyteArray array, jbyte *elems, jint mode) {
367 env->ReleaseByteArrayElements(array, elems, mode);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800368}
369
Andy Hung0e9e2f32015-04-13 23:47:18 -0700370static inline
371jshort *envGetArrayElements(JNIEnv *env, jshortArray array, jboolean *isCopy) {
372 return env->GetShortArrayElements(array, isCopy);
373}
374
375static inline
376void envReleaseArrayElements(JNIEnv *env, jshortArray array, jshort *elems, jint mode) {
377 env->ReleaseShortArrayElements(array, elems, mode);
378}
379
380static inline
381jfloat *envGetArrayElements(JNIEnv *env, jfloatArray array, jboolean *isCopy) {
382 return env->GetFloatArrayElements(array, isCopy);
383}
384
385static inline
386void envReleaseArrayElements(JNIEnv *env, jfloatArray array, jfloat *elems, jint mode) {
387 env->ReleaseFloatArrayElements(array, elems, mode);
388}
389
390static inline
391jint interpretReadSizeError(ssize_t readSize) {
392 ALOGE_IF(readSize != WOULD_BLOCK, "Error %zd during AudioRecord native read", readSize);
393 switch (readSize) {
394 case WOULD_BLOCK:
395 return (jint)0;
396 case BAD_VALUE:
397 return (jint)AUDIO_JAVA_BAD_VALUE;
398 default:
399 // may be possible for other errors such as
400 // NO_INIT to happen if restoreRecord_l fails.
401 case INVALID_OPERATION:
402 return (jint)AUDIO_JAVA_INVALID_OPERATION;
403 }
404}
405
406template <typename T>
407static jint android_media_AudioRecord_readInArray(JNIEnv *env, jobject thiz,
408 T javaAudioData,
409 jint offsetInSamples, jint sizeInSamples,
410 jboolean isReadBlocking) {
Andy Hung58b0f3f2015-03-27 17:59:45 -0700411 // get the audio recorder from which we'll read new audio samples
412 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
413 if (lpRecorder == NULL) {
414 ALOGE("Unable to retrieve AudioRecord object");
415 return (jint)AUDIO_JAVA_INVALID_OPERATION;
416 }
417
418 if (javaAudioData == NULL) {
Andy Hung0e9e2f32015-04-13 23:47:18 -0700419 ALOGE("Invalid Java array to store recorded audio");
420 return (jint)AUDIO_JAVA_BAD_VALUE;
421 }
Andy Hung58b0f3f2015-03-27 17:59:45 -0700422
Andy Hung58b0f3f2015-03-27 17:59:45 -0700423 // NOTE: We may use GetPrimitiveArrayCritical() when the JNI implementation changes in such
424 // a way that it becomes much more efficient. When doing so, we will have to prevent the
425 // AudioSystem callback to be called while in critical section (in case of media server
426 // process crash for instance)
Andy Hung0e9e2f32015-04-13 23:47:18 -0700427
428 // get the pointer to where we'll record the audio
429 auto *recordBuff = envGetArrayElements(env, javaAudioData, NULL);
Andy Hung58b0f3f2015-03-27 17:59:45 -0700430 if (recordBuff == NULL) {
431 ALOGE("Error retrieving destination for recorded audio data");
432 return (jint)AUDIO_JAVA_BAD_VALUE;
433 }
434
435 // read the new audio data from the native AudioRecord object
Andy Hung0e9e2f32015-04-13 23:47:18 -0700436 const size_t sizeInBytes = sizeInSamples * sizeof(*recordBuff);
437 ssize_t readSize = lpRecorder->read(
438 recordBuff + offsetInSamples, sizeInBytes, isReadBlocking == JNI_TRUE /* blocking */);
Andy Hung58b0f3f2015-03-27 17:59:45 -0700439
Andy Hung0e9e2f32015-04-13 23:47:18 -0700440 envReleaseArrayElements(env, javaAudioData, recordBuff, 0);
Andy Hung58b0f3f2015-03-27 17:59:45 -0700441
442 if (readSize < 0) {
Andy Hung0e9e2f32015-04-13 23:47:18 -0700443 return interpretReadSizeError(readSize);
Andy Hung58b0f3f2015-03-27 17:59:45 -0700444 }
Andy Hung0e9e2f32015-04-13 23:47:18 -0700445 return (jint)(readSize / sizeof(*recordBuff));
Andy Hung58b0f3f2015-03-27 17:59:45 -0700446}
447
448// ----------------------------------------------------------------------------
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800449static jint android_media_AudioRecord_readInDirectBuffer(JNIEnv *env, jobject thiz,
Andy Hung0e9e2f32015-04-13 23:47:18 -0700450 jobject jBuffer, jint sizeInBytes,
451 jboolean isReadBlocking) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800452 // get the audio recorder from which we'll read new audio samples
Eric Laurent532bc1c2012-04-20 12:45:03 -0700453 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
Glenn Kasten18db49a2012-03-12 16:29:55 -0700454 if (lpRecorder==NULL)
Andy Hung0e9e2f32015-04-13 23:47:18 -0700455 return (jint)AUDIO_JAVA_INVALID_OPERATION;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800456
457 // direct buffer and direct access supported?
458 long capacity = env->GetDirectBufferCapacity(jBuffer);
Glenn Kasten18db49a2012-03-12 16:29:55 -0700459 if (capacity == -1) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800460 // buffer direct access is not supported
Steve Block3762c312012-01-06 19:20:56 +0000461 ALOGE("Buffer direct access is not supported, can't record");
Andy Hung0e9e2f32015-04-13 23:47:18 -0700462 return (jint)AUDIO_JAVA_BAD_VALUE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800463 }
Steve Block71f2cf12011-10-20 11:56:00 +0100464 //ALOGV("capacity = %ld", capacity);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800465 jbyte* nativeFromJavaBuf = (jbyte*) env->GetDirectBufferAddress(jBuffer);
Glenn Kasten18db49a2012-03-12 16:29:55 -0700466 if (nativeFromJavaBuf==NULL) {
Steve Block3762c312012-01-06 19:20:56 +0000467 ALOGE("Buffer direct access is not supported, can't record");
Andy Hung0e9e2f32015-04-13 23:47:18 -0700468 return (jint)AUDIO_JAVA_BAD_VALUE;
Glenn Kasten18db49a2012-03-12 16:29:55 -0700469 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800470
471 // read new data from the recorder
Eric Laurent357263d2013-09-09 10:31:59 -0700472 ssize_t readSize = lpRecorder->read(nativeFromJavaBuf,
Andy Hung0e9e2f32015-04-13 23:47:18 -0700473 capacity < sizeInBytes ? capacity : sizeInBytes,
474 isReadBlocking == JNI_TRUE /* blocking */);
Eric Laurent357263d2013-09-09 10:31:59 -0700475 if (readSize < 0) {
Andy Hung0e9e2f32015-04-13 23:47:18 -0700476 return interpretReadSizeError(readSize);
Eric Laurent357263d2013-09-09 10:31:59 -0700477 }
478 return (jint)readSize;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800479}
480
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800481// ----------------------------------------------------------------------------
Andy Hung864ae672015-04-14 11:06:48 -0700482static jint android_media_AudioRecord_get_native_frame_count(JNIEnv *env, jobject thiz) {
483 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
484 if (lpRecorder == NULL) {
485 jniThrowException(env, "java/lang/IllegalStateException",
486 "Unable to retrieve AudioRecord pointer for getNativeFrameCount()");
487 return (jint)AUDIO_JAVA_ERROR;
488 }
489 return lpRecorder->frameCount();
490}
491
492// ----------------------------------------------------------------------------
Glenn Kasten18db49a2012-03-12 16:29:55 -0700493static jint android_media_AudioRecord_set_marker_pos(JNIEnv *env, jobject thiz,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800494 jint markerPos) {
Glenn Kasten18db49a2012-03-12 16:29:55 -0700495
Eric Laurent532bc1c2012-04-20 12:45:03 -0700496 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
497 if (lpRecorder == NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800498 jniThrowException(env, "java/lang/IllegalStateException",
499 "Unable to retrieve AudioRecord pointer for setMarkerPosition()");
Eric Laurentbc11a692014-05-16 12:19:25 -0700500 return (jint)AUDIO_JAVA_ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800501 }
Eric Laurentbc11a692014-05-16 12:19:25 -0700502 return nativeToJavaStatus( lpRecorder->setMarkerPosition(markerPos) );
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800503}
504
505
506// ----------------------------------------------------------------------------
507static jint android_media_AudioRecord_get_marker_pos(JNIEnv *env, jobject thiz) {
Glenn Kasten18db49a2012-03-12 16:29:55 -0700508
Eric Laurent532bc1c2012-04-20 12:45:03 -0700509 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800510 uint32_t markerPos = 0;
Glenn Kasten18db49a2012-03-12 16:29:55 -0700511
Eric Laurent532bc1c2012-04-20 12:45:03 -0700512 if (lpRecorder == NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800513 jniThrowException(env, "java/lang/IllegalStateException",
514 "Unable to retrieve AudioRecord pointer for getMarkerPosition()");
Eric Laurentbc11a692014-05-16 12:19:25 -0700515 return (jint)AUDIO_JAVA_ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800516 }
Eric Laurent532bc1c2012-04-20 12:45:03 -0700517 lpRecorder->getMarkerPosition(&markerPos);
518 return (jint)markerPos;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800519}
520
521
522// ----------------------------------------------------------------------------
523static jint android_media_AudioRecord_set_pos_update_period(JNIEnv *env, jobject thiz,
524 jint period) {
Glenn Kasten18db49a2012-03-12 16:29:55 -0700525
Eric Laurent532bc1c2012-04-20 12:45:03 -0700526 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
Glenn Kasten18db49a2012-03-12 16:29:55 -0700527
Eric Laurent532bc1c2012-04-20 12:45:03 -0700528 if (lpRecorder == NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800529 jniThrowException(env, "java/lang/IllegalStateException",
530 "Unable to retrieve AudioRecord pointer for setPositionUpdatePeriod()");
Eric Laurentbc11a692014-05-16 12:19:25 -0700531 return (jint)AUDIO_JAVA_ERROR;
Glenn Kasten18db49a2012-03-12 16:29:55 -0700532 }
Eric Laurentbc11a692014-05-16 12:19:25 -0700533 return nativeToJavaStatus( lpRecorder->setPositionUpdatePeriod(period) );
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800534}
535
536
537// ----------------------------------------------------------------------------
538static jint android_media_AudioRecord_get_pos_update_period(JNIEnv *env, jobject thiz) {
Glenn Kasten18db49a2012-03-12 16:29:55 -0700539
Eric Laurent532bc1c2012-04-20 12:45:03 -0700540 sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800541 uint32_t period = 0;
Glenn Kasten18db49a2012-03-12 16:29:55 -0700542
Eric Laurent532bc1c2012-04-20 12:45:03 -0700543 if (lpRecorder == NULL) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800544 jniThrowException(env, "java/lang/IllegalStateException",
545 "Unable to retrieve AudioRecord pointer for getPositionUpdatePeriod()");
Eric Laurentbc11a692014-05-16 12:19:25 -0700546 return (jint)AUDIO_JAVA_ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800547 }
Eric Laurent532bc1c2012-04-20 12:45:03 -0700548 lpRecorder->getPositionUpdatePeriod(&period);
549 return (jint)period;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800550}
551
552
553// ----------------------------------------------------------------------------
554// returns the minimum required size for the successful creation of an AudioRecord instance.
555// returns 0 if the parameter combination is not supported.
556// return -1 if there was an error querying the buffer size.
557static jint android_media_AudioRecord_get_min_buff_size(JNIEnv *env, jobject thiz,
Glenn Kasten5b8fd442013-11-14 09:44:14 -0800558 jint sampleRateInHertz, jint channelCount, jint audioFormat) {
Chia-chi Yehc3308072010-08-19 17:14:36 +0800559
Eric Laurent532bc1c2012-04-20 12:45:03 -0700560 ALOGV(">> android_media_AudioRecord_get_min_buff_size(%d, %d, %d)",
Glenn Kasten5b8fd442013-11-14 09:44:14 -0800561 sampleRateInHertz, channelCount, audioFormat);
Chia-chi Yehc3308072010-08-19 17:14:36 +0800562
Glenn Kastenfd1e3df2012-11-13 15:21:06 -0800563 size_t frameCount = 0;
Glenn Kastena5a42382013-12-19 12:34:56 -0800564 audio_format_t format = audioFormatToNative(audioFormat);
Chia-chi Yehc3308072010-08-19 17:14:36 +0800565 status_t result = AudioRecord::getMinFrameCount(&frameCount,
566 sampleRateInHertz,
Glenn Kastena5a42382013-12-19 12:34:56 -0800567 format,
Glenn Kasten5b8fd442013-11-14 09:44:14 -0800568 audio_channel_in_mask_from_count(channelCount));
Chia-chi Yehc3308072010-08-19 17:14:36 +0800569
570 if (result == BAD_VALUE) {
571 return 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800572 }
Chia-chi Yehc3308072010-08-19 17:14:36 +0800573 if (result != NO_ERROR) {
574 return -1;
575 }
Glenn Kasten5b8fd442013-11-14 09:44:14 -0800576 return frameCount * channelCount * audio_bytes_per_sample(format);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800577}
578
579
580// ----------------------------------------------------------------------------
581// ----------------------------------------------------------------------------
582static JNINativeMethod gMethods[] = {
583 // name, signature, funcPtr
Eric Laurent505e5c82012-03-29 15:19:36 -0700584 {"native_start", "(II)I", (void *)android_media_AudioRecord_start},
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800585 {"native_stop", "()V", (void *)android_media_AudioRecord_stop},
Jean-Michel Trivi701d6ff2014-07-16 07:51:22 -0700586 {"native_setup", "(Ljava/lang/Object;Ljava/lang/Object;IIII[I)I",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800587 (void *)android_media_AudioRecord_setup},
588 {"native_finalize", "()V", (void *)android_media_AudioRecord_finalize},
589 {"native_release", "()V", (void *)android_media_AudioRecord_release},
Glenn Kasten18db49a2012-03-12 16:29:55 -0700590 {"native_read_in_byte_array",
Andy Hung0e9e2f32015-04-13 23:47:18 -0700591 "([BIIZ)I",
592 (void *)android_media_AudioRecord_readInArray<jbyteArray>},
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800593 {"native_read_in_short_array",
Andy Hung0e9e2f32015-04-13 23:47:18 -0700594 "([SIIZ)I",
595 (void *)android_media_AudioRecord_readInArray<jshortArray>},
Andy Hung58b0f3f2015-03-27 17:59:45 -0700596 {"native_read_in_float_array",
Andy Hung0e9e2f32015-04-13 23:47:18 -0700597 "([FIIZ)I",
598 (void *)android_media_AudioRecord_readInArray<jfloatArray>},
599 {"native_read_in_direct_buffer","(Ljava/lang/Object;IZ)I",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800600 (void *)android_media_AudioRecord_readInDirectBuffer},
Andy Hung864ae672015-04-14 11:06:48 -0700601 {"native_get_native_frame_count",
602 "()I", (void *)android_media_AudioRecord_get_native_frame_count},
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800603 {"native_set_marker_pos","(I)I", (void *)android_media_AudioRecord_set_marker_pos},
604 {"native_get_marker_pos","()I", (void *)android_media_AudioRecord_get_marker_pos},
605 {"native_set_pos_update_period",
606 "(I)I", (void *)android_media_AudioRecord_set_pos_update_period},
607 {"native_get_pos_update_period",
608 "()I", (void *)android_media_AudioRecord_get_pos_update_period},
609 {"native_get_min_buff_size",
610 "(III)I", (void *)android_media_AudioRecord_get_min_buff_size},
611};
612
613// field names found in android/media/AudioRecord.java
614#define JAVA_POSTEVENT_CALLBACK_NAME "postEventFromNative"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800615#define JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME "mNativeRecorderInJavaObj"
616#define JAVA_NATIVECALLBACKINFO_FIELD_NAME "mNativeCallbackCookie"
617
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800618// ----------------------------------------------------------------------------
619int register_android_media_AudioRecord(JNIEnv *env)
620{
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800621 javaAudioRecordFields.postNativeEventInJava = NULL;
622 javaAudioRecordFields.nativeRecorderInJavaObj = NULL;
623 javaAudioRecordFields.nativeCallbackCookie = NULL;
Glenn Kasten18db49a2012-03-12 16:29:55 -0700624
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800625
626 // Get the AudioRecord class
Andreas Gampeed6b9df2014-11-20 22:02:20 -0800627 jclass audioRecordClass = FindClassOrDie(env, kClassPathName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800628 // Get the postEvent method
Andreas Gampeed6b9df2014-11-20 22:02:20 -0800629 javaAudioRecordFields.postNativeEventInJava = GetStaticMethodIDOrDie(env,
630 audioRecordClass, JAVA_POSTEVENT_CALLBACK_NAME,
631 "(Ljava/lang/Object;IIILjava/lang/Object;)V");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800632
633 // Get the variables
634 // mNativeRecorderInJavaObj
Andreas Gampeed6b9df2014-11-20 22:02:20 -0800635 javaAudioRecordFields.nativeRecorderInJavaObj = GetFieldIDOrDie(env,
636 audioRecordClass, JAVA_NATIVERECORDERINJAVAOBJ_FIELD_NAME, "J");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800637 // mNativeCallbackCookie
Andreas Gampeed6b9df2014-11-20 22:02:20 -0800638 javaAudioRecordFields.nativeCallbackCookie = GetFieldIDOrDie(env,
639 audioRecordClass, JAVA_NATIVECALLBACKINFO_FIELD_NAME, "J");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800640
Jean-Michel Trivi701d6ff2014-07-16 07:51:22 -0700641 // Get the AudioAttributes class and fields
Andreas Gampeed6b9df2014-11-20 22:02:20 -0800642 jclass audioAttrClass = FindClassOrDie(env, kAudioAttributesClassPathName);
643 javaAudioAttrFields.fieldRecSource = GetFieldIDOrDie(env, audioAttrClass, "mSource", "I");
644 javaAudioAttrFields.fieldFlags = GetFieldIDOrDie(env, audioAttrClass, "mFlags", "I");
645 javaAudioAttrFields.fieldFormattedTags = GetFieldIDOrDie(env,
646 audioAttrClass, "mFormattedTags", "Ljava/lang/String;");
Jean-Michel Trivi701d6ff2014-07-16 07:51:22 -0700647
Andreas Gampeed6b9df2014-11-20 22:02:20 -0800648 return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800649}
650
651// ----------------------------------------------------------------------------