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//#define LOG_NDEBUG 0
17
18#define LOG_TAG "AudioTrack-JNI"
19
20#include "android_media_AudioTrack.h"
21
22#include <nativehelper/JNIHelp.h>
23#include <nativehelper/JniConstants.h>
24#include "core_jni_helpers.h"
25
26#include <nativehelper/ScopedBytes.h>
27
28#include <utils/Log.h>
29#include <media/AudioSystem.h>
30#include <media/AudioTrack.h>
31
32#include <binder/MemoryHeapBase.h>
33#include <binder/MemoryBase.h>
34
35#include "android_media_AudioFormat.h"
36#include "android_media_AudioErrors.h"
37#include "android_media_PlaybackParams.h"
38#include "android_media_DeviceCallback.h"
39#include "android_media_VolumeShaper.h"
40
41#include <cinttypes>
42
43// ----------------------------------------------------------------------------
44
45using namespace android;
46
47// ----------------------------------------------------------------------------
48static const char* const kClassPathName = "android/media/AudioTrack";
49static const char* const kAudioAttributesClassPathName = "android/media/AudioAttributes";
50
51struct audio_track_fields_t {
52    // these fields provide access from C++ to the...
53    jmethodID postNativeEventInJava; //... event post callback method
54    jfieldID  nativeTrackInJavaObj;  // stores in Java the native AudioTrack object
55    jfieldID  jniData;      // stores in Java additional resources used by the native AudioTrack
56    jfieldID  fieldStreamType; // ... mStreamType field in the AudioTrack Java object
57};
58struct audio_attributes_fields_t {
59    jfieldID  fieldUsage;        // AudioAttributes.mUsage
60    jfieldID  fieldContentType;  // AudioAttributes.mContentType
61    jfieldID  fieldFlags;        // AudioAttributes.mFlags
62    jfieldID  fieldFormattedTags;// AudioAttributes.mFormattedTags
63};
64static audio_track_fields_t      javaAudioTrackFields;
65static audio_attributes_fields_t javaAudioAttrFields;
66static PlaybackParams::fields_t gPlaybackParamsFields;
67static VolumeShaperHelper::fields_t gVolumeShaperFields;
68
69struct audiotrack_callback_cookie {
70    jclass      audioTrack_class;
71    jobject     audioTrack_ref;
72    bool        busy;
73    Condition   cond;
74};
75
76// keep these values in sync with AudioTrack.java
77#define MODE_STATIC 0
78#define MODE_STREAM 1
79
80// ----------------------------------------------------------------------------
81class AudioTrackJniStorage {
82    public:
83        sp<MemoryHeapBase>         mMemHeap;
84        sp<MemoryBase>             mMemBase;
85        audiotrack_callback_cookie mCallbackData;
86        sp<JNIDeviceCallback>      mDeviceCallback;
87
88    AudioTrackJniStorage() {
89        mCallbackData.audioTrack_class = 0;
90        mCallbackData.audioTrack_ref = 0;
91    }
92
93    ~AudioTrackJniStorage() {
94        mMemBase.clear();
95        mMemHeap.clear();
96    }
97
98    bool allocSharedMem(int sizeInBytes) {
99        mMemHeap = new MemoryHeapBase(sizeInBytes, 0, "AudioTrack Heap Base");
100        if (mMemHeap->getHeapID() < 0) {
101            return false;
102        }
103        mMemBase = new MemoryBase(mMemHeap, 0, sizeInBytes);
104        return true;
105    }
106};
107
108static Mutex sLock;
109static SortedVector <audiotrack_callback_cookie *> sAudioTrackCallBackCookies;
110
111// ----------------------------------------------------------------------------
112#define DEFAULT_OUTPUT_SAMPLE_RATE   44100
113
114#define AUDIOTRACK_ERROR_SETUP_AUDIOSYSTEM         (-16)
115#define AUDIOTRACK_ERROR_SETUP_INVALIDCHANNELMASK  (-17)
116#define AUDIOTRACK_ERROR_SETUP_INVALIDFORMAT       (-18)
117#define AUDIOTRACK_ERROR_SETUP_INVALIDSTREAMTYPE   (-19)
118#define AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED    (-20)
119
120// ----------------------------------------------------------------------------
121static void audioCallback(int event, void* user, void *info) {
122
123    audiotrack_callback_cookie *callbackInfo = (audiotrack_callback_cookie *)user;
124    {
125        Mutex::Autolock l(sLock);
126        if (sAudioTrackCallBackCookies.indexOf(callbackInfo) < 0) {
127            return;
128        }
129        callbackInfo->busy = true;
130    }
131
132    switch (event) {
133    case AudioTrack::EVENT_MARKER: {
134        JNIEnv *env = AndroidRuntime::getJNIEnv();
135        if (user != NULL && env != NULL) {
136            env->CallStaticVoidMethod(
137                callbackInfo->audioTrack_class,
138                javaAudioTrackFields.postNativeEventInJava,
139                callbackInfo->audioTrack_ref, event, 0,0, NULL);
140            if (env->ExceptionCheck()) {
141                env->ExceptionDescribe();
142                env->ExceptionClear();
143            }
144        }
145        } break;
146
147    case AudioTrack::EVENT_NEW_POS: {
148        JNIEnv *env = AndroidRuntime::getJNIEnv();
149        if (user != NULL && env != NULL) {
150            env->CallStaticVoidMethod(
151                callbackInfo->audioTrack_class,
152                javaAudioTrackFields.postNativeEventInJava,
153                callbackInfo->audioTrack_ref, event, 0,0, NULL);
154            if (env->ExceptionCheck()) {
155                env->ExceptionDescribe();
156                env->ExceptionClear();
157            }
158        }
159        } break;
160    }
161
162    {
163        Mutex::Autolock l(sLock);
164        callbackInfo->busy = false;
165        callbackInfo->cond.broadcast();
166    }
167}
168
169
170// ----------------------------------------------------------------------------
171static sp<AudioTrack> getAudioTrack(JNIEnv* env, jobject thiz)
172{
173    Mutex::Autolock l(sLock);
174    AudioTrack* const at =
175            (AudioTrack*)env->GetLongField(thiz, javaAudioTrackFields.nativeTrackInJavaObj);
176    return sp<AudioTrack>(at);
177}
178
179static sp<AudioTrack> setAudioTrack(JNIEnv* env, jobject thiz, const sp<AudioTrack>& at)
180{
181    Mutex::Autolock l(sLock);
182    sp<AudioTrack> old =
183            (AudioTrack*)env->GetLongField(thiz, javaAudioTrackFields.nativeTrackInJavaObj);
184    if (at.get()) {
185        at->incStrong((void*)setAudioTrack);
186    }
187    if (old != 0) {
188        old->decStrong((void*)setAudioTrack);
189    }
190    env->SetLongField(thiz, javaAudioTrackFields.nativeTrackInJavaObj, (jlong)at.get());
191    return old;
192}
193
194// ----------------------------------------------------------------------------
195sp<AudioTrack> android_media_AudioTrack_getAudioTrack(JNIEnv* env, jobject audioTrackObj) {
196    return getAudioTrack(env, audioTrackObj);
197}
198
199// This function converts Java channel masks to a native channel mask.
200// validity should be checked with audio_is_output_channel().
201static inline audio_channel_mask_t nativeChannelMaskFromJavaChannelMasks(
202        jint channelPositionMask, jint channelIndexMask)
203{
204    if (channelIndexMask != 0) {  // channel index mask takes priority
205        // To convert to a native channel mask, the Java channel index mask
206        // requires adding the index representation.
207        return audio_channel_mask_from_representation_and_bits(
208                        AUDIO_CHANNEL_REPRESENTATION_INDEX,
209                        channelIndexMask);
210    }
211    // To convert to a native channel mask, the Java channel position mask
212    // requires a shift by 2 to skip the two deprecated channel
213    // configurations "default" and "mono".
214    return (audio_channel_mask_t)(channelPositionMask >> 2);
215}
216
217// ----------------------------------------------------------------------------
218static jint
219android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this, jobject jaa,
220        jintArray jSampleRate, jint channelPositionMask, jint channelIndexMask,
221        jint audioFormat, jint buffSizeInBytes, jint memoryMode, jintArray jSession,
222        jlong nativeAudioTrack) {
223
224    ALOGV("sampleRates=%p, channel mask=%x, index mask=%x, audioFormat(Java)=%d, buffSize=%d"
225        "nativeAudioTrack=0x%" PRIX64,
226        jSampleRate, channelPositionMask, channelIndexMask, audioFormat, buffSizeInBytes,
227        nativeAudioTrack);
228
229    sp<AudioTrack> lpTrack = 0;
230
231    if (jSession == NULL) {
232        ALOGE("Error creating AudioTrack: invalid session ID pointer");
233        return (jint) AUDIO_JAVA_ERROR;
234    }
235
236    jint* nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
237    if (nSession == NULL) {
238        ALOGE("Error creating AudioTrack: Error retrieving session id pointer");
239        return (jint) AUDIO_JAVA_ERROR;
240    }
241    audio_session_t sessionId = (audio_session_t) nSession[0];
242    env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
243    nSession = NULL;
244
245    AudioTrackJniStorage* lpJniStorage = NULL;
246
247    audio_attributes_t *paa = NULL;
248
249    jclass clazz = env->GetObjectClass(thiz);
250    if (clazz == NULL) {
251        ALOGE("Can't find %s when setting up callback.", kClassPathName);
252        return (jint) AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED;
253    }
254
255    // if we pass in an existing *Native* AudioTrack, we don't need to create/initialize one.
256    if (nativeAudioTrack == 0) {
257        if (jaa == 0) {
258            ALOGE("Error creating AudioTrack: invalid audio attributes");
259            return (jint) AUDIO_JAVA_ERROR;
260        }
261
262        if (jSampleRate == 0) {
263            ALOGE("Error creating AudioTrack: invalid sample rates");
264            return (jint) AUDIO_JAVA_ERROR;
265        }
266
267        int* sampleRates = env->GetIntArrayElements(jSampleRate, NULL);
268        int sampleRateInHertz = sampleRates[0];
269        env->ReleaseIntArrayElements(jSampleRate, sampleRates, JNI_ABORT);
270
271        // Invalid channel representations are caught by !audio_is_output_channel() below.
272        audio_channel_mask_t nativeChannelMask = nativeChannelMaskFromJavaChannelMasks(
273                channelPositionMask, channelIndexMask);
274        if (!audio_is_output_channel(nativeChannelMask)) {
275            ALOGE("Error creating AudioTrack: invalid native channel mask %#x.", nativeChannelMask);
276            return (jint) AUDIOTRACK_ERROR_SETUP_INVALIDCHANNELMASK;
277        }
278
279        uint32_t channelCount = audio_channel_count_from_out_mask(nativeChannelMask);
280
281        // check the format.
282        // This function was called from Java, so we compare the format against the Java constants
283        audio_format_t format = audioFormatToNative(audioFormat);
284        if (format == AUDIO_FORMAT_INVALID) {
285            ALOGE("Error creating AudioTrack: unsupported audio format %d.", audioFormat);
286            return (jint) AUDIOTRACK_ERROR_SETUP_INVALIDFORMAT;
287        }
288
289        // compute the frame count
290        size_t frameCount;
291        if (audio_is_linear_pcm(format)) {
292            const size_t bytesPerSample = audio_bytes_per_sample(format);
293            frameCount = buffSizeInBytes / (channelCount * bytesPerSample);
294        } else {
295            frameCount = buffSizeInBytes;
296        }
297
298        // create the native AudioTrack object
299        lpTrack = new AudioTrack();
300
301        // read the AudioAttributes values
302        paa = (audio_attributes_t *) calloc(1, sizeof(audio_attributes_t));
303        const jstring jtags =
304                (jstring) env->GetObjectField(jaa, javaAudioAttrFields.fieldFormattedTags);
305        const char* tags = env->GetStringUTFChars(jtags, NULL);
306        // copying array size -1, char array for tags was calloc'd, no need to NULL-terminate it
307        strncpy(paa->tags, tags, AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1);
308        env->ReleaseStringUTFChars(jtags, tags);
309        paa->usage = (audio_usage_t) env->GetIntField(jaa, javaAudioAttrFields.fieldUsage);
310        paa->content_type =
311                (audio_content_type_t) env->GetIntField(jaa, javaAudioAttrFields.fieldContentType);
312        paa->flags = env->GetIntField(jaa, javaAudioAttrFields.fieldFlags);
313
314        ALOGV("AudioTrack_setup for usage=%d content=%d flags=0x%#x tags=%s",
315                paa->usage, paa->content_type, paa->flags, paa->tags);
316
317        // initialize the callback information:
318        // this data will be passed with every AudioTrack callback
319        lpJniStorage = new AudioTrackJniStorage();
320        lpJniStorage->mCallbackData.audioTrack_class = (jclass)env->NewGlobalRef(clazz);
321        // we use a weak reference so the AudioTrack object can be garbage collected.
322        lpJniStorage->mCallbackData.audioTrack_ref = env->NewGlobalRef(weak_this);
323        lpJniStorage->mCallbackData.busy = false;
324
325        // initialize the native AudioTrack object
326        status_t status = NO_ERROR;
327        switch (memoryMode) {
328        case MODE_STREAM:
329
330            status = lpTrack->set(
331                    AUDIO_STREAM_DEFAULT,// stream type, but more info conveyed in paa (last argument)
332                    sampleRateInHertz,
333                    format,// word length, PCM
334                    nativeChannelMask,
335                    frameCount,
336                    AUDIO_OUTPUT_FLAG_NONE,
337                    audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user)
338                    0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack
339                    0,// shared mem
340                    true,// thread can call Java
341                    sessionId,// audio session ID
342                    AudioTrack::TRANSFER_SYNC,
343                    NULL,                         // default offloadInfo
344                    -1, -1,                       // default uid, pid values
345                    paa);
346            break;
347
348        case MODE_STATIC:
349            // AudioTrack is using shared memory
350
351            if (!lpJniStorage->allocSharedMem(buffSizeInBytes)) {
352                ALOGE("Error creating AudioTrack in static mode: error creating mem heap base");
353                goto native_init_failure;
354            }
355
356            status = lpTrack->set(
357                    AUDIO_STREAM_DEFAULT,// stream type, but more info conveyed in paa (last argument)
358                    sampleRateInHertz,
359                    format,// word length, PCM
360                    nativeChannelMask,
361                    frameCount,
362                    AUDIO_OUTPUT_FLAG_NONE,
363                    audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user));
364                    0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack
365                    lpJniStorage->mMemBase,// shared mem
366                    true,// thread can call Java
367                    sessionId,// audio session ID
368                    AudioTrack::TRANSFER_SHARED,
369                    NULL,                         // default offloadInfo
370                    -1, -1,                       // default uid, pid values
371                    paa);
372            break;
373
374        default:
375            ALOGE("Unknown mode %d", memoryMode);
376            goto native_init_failure;
377        }
378
379        if (status != NO_ERROR) {
380            ALOGE("Error %d initializing AudioTrack", status);
381            goto native_init_failure;
382        }
383    } else {  // end if (nativeAudioTrack == 0)
384        lpTrack = (AudioTrack*)nativeAudioTrack;
385        // TODO: We need to find out which members of the Java AudioTrack might
386        // need to be initialized from the Native AudioTrack
387        // these are directly returned from getters:
388        //  mSampleRate
389        //  mAudioFormat
390        //  mStreamType
391        //  mChannelConfiguration
392        //  mChannelCount
393        //  mState (?)
394        //  mPlayState (?)
395        // these may be used internally (Java AudioTrack.audioParamCheck():
396        //  mChannelMask
397        //  mChannelIndexMask
398        //  mDataLoadMode
399
400        // initialize the callback information:
401        // this data will be passed with every AudioTrack callback
402        lpJniStorage = new AudioTrackJniStorage();
403        lpJniStorage->mCallbackData.audioTrack_class = (jclass)env->NewGlobalRef(clazz);
404        // we use a weak reference so the AudioTrack object can be garbage collected.
405        lpJniStorage->mCallbackData.audioTrack_ref = env->NewGlobalRef(weak_this);
406        lpJniStorage->mCallbackData.busy = false;
407    }
408
409    nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
410    if (nSession == NULL) {
411        ALOGE("Error creating AudioTrack: Error retrieving session id pointer");
412        goto native_init_failure;
413    }
414    // read the audio session ID back from AudioTrack in case we create a new session
415    nSession[0] = lpTrack->getSessionId();
416    env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
417    nSession = NULL;
418
419    {
420        const jint elements[1] = { (jint) lpTrack->getSampleRate() };
421        env->SetIntArrayRegion(jSampleRate, 0, 1, elements);
422    }
423
424    {   // scope for the lock
425        Mutex::Autolock l(sLock);
426        sAudioTrackCallBackCookies.add(&lpJniStorage->mCallbackData);
427    }
428    // save our newly created C++ AudioTrack in the "nativeTrackInJavaObj" field
429    // of the Java object (in mNativeTrackInJavaObj)
430    setAudioTrack(env, thiz, lpTrack);
431
432    // save the JNI resources so we can free them later
433    //ALOGV("storing lpJniStorage: %x\n", (long)lpJniStorage);
434    env->SetLongField(thiz, javaAudioTrackFields.jniData, (jlong)lpJniStorage);
435
436    // since we had audio attributes, the stream type was derived from them during the
437    // creation of the native AudioTrack: push the same value to the Java object
438    env->SetIntField(thiz, javaAudioTrackFields.fieldStreamType, (jint) lpTrack->streamType());
439    if (paa != NULL) {
440        // audio attributes were copied in AudioTrack creation
441        free(paa);
442        paa = NULL;
443    }
444
445
446    return (jint) AUDIO_JAVA_SUCCESS;
447
448    // failures:
449native_init_failure:
450    if (paa != NULL) {
451        free(paa);
452    }
453    if (nSession != NULL) {
454        env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
455    }
456    env->DeleteGlobalRef(lpJniStorage->mCallbackData.audioTrack_class);
457    env->DeleteGlobalRef(lpJniStorage->mCallbackData.audioTrack_ref);
458    delete lpJniStorage;
459    env->SetLongField(thiz, javaAudioTrackFields.jniData, 0);
460
461    // lpTrack goes out of scope, so reference count drops to zero
462    return (jint) AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED;
463}
464
465// ----------------------------------------------------------------------------
466static void
467android_media_AudioTrack_start(JNIEnv *env, jobject thiz)
468{
469    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
470    if (lpTrack == NULL) {
471        jniThrowException(env, "java/lang/IllegalStateException",
472            "Unable to retrieve AudioTrack pointer for start()");
473        return;
474    }
475
476    lpTrack->start();
477}
478
479
480// ----------------------------------------------------------------------------
481static void
482android_media_AudioTrack_stop(JNIEnv *env, jobject thiz)
483{
484    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
485    if (lpTrack == NULL) {
486        jniThrowException(env, "java/lang/IllegalStateException",
487            "Unable to retrieve AudioTrack pointer for stop()");
488        return;
489    }
490
491    lpTrack->stop();
492}
493
494
495// ----------------------------------------------------------------------------
496static void
497android_media_AudioTrack_pause(JNIEnv *env, jobject thiz)
498{
499    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
500    if (lpTrack == NULL) {
501        jniThrowException(env, "java/lang/IllegalStateException",
502            "Unable to retrieve AudioTrack pointer for pause()");
503        return;
504    }
505
506    lpTrack->pause();
507}
508
509
510// ----------------------------------------------------------------------------
511static void
512android_media_AudioTrack_flush(JNIEnv *env, jobject thiz)
513{
514    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
515    if (lpTrack == NULL) {
516        jniThrowException(env, "java/lang/IllegalStateException",
517            "Unable to retrieve AudioTrack pointer for flush()");
518        return;
519    }
520
521    lpTrack->flush();
522}
523
524// ----------------------------------------------------------------------------
525static void
526android_media_AudioTrack_set_volume(JNIEnv *env, jobject thiz, jfloat leftVol, jfloat rightVol )
527{
528    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
529    if (lpTrack == NULL) {
530        jniThrowException(env, "java/lang/IllegalStateException",
531            "Unable to retrieve AudioTrack pointer for setVolume()");
532        return;
533    }
534
535    lpTrack->setVolume(leftVol, rightVol);
536}
537
538// ----------------------------------------------------------------------------
539
540#define CALLBACK_COND_WAIT_TIMEOUT_MS 1000
541static void android_media_AudioTrack_release(JNIEnv *env,  jobject thiz) {
542    sp<AudioTrack> lpTrack = setAudioTrack(env, thiz, 0);
543    if (lpTrack == NULL) {
544        return;
545    }
546    //ALOGV("deleting lpTrack: %x\n", (int)lpTrack);
547
548    // delete the JNI data
549    AudioTrackJniStorage* pJniStorage = (AudioTrackJniStorage *)env->GetLongField(
550        thiz, javaAudioTrackFields.jniData);
551    // reset the native resources in the Java object so any attempt to access
552    // them after a call to release fails.
553    env->SetLongField(thiz, javaAudioTrackFields.jniData, 0);
554
555    if (pJniStorage) {
556        Mutex::Autolock l(sLock);
557        audiotrack_callback_cookie *lpCookie = &pJniStorage->mCallbackData;
558        //ALOGV("deleting pJniStorage: %x\n", (int)pJniStorage);
559        while (lpCookie->busy) {
560            if (lpCookie->cond.waitRelative(sLock,
561                                            milliseconds(CALLBACK_COND_WAIT_TIMEOUT_MS)) !=
562                                                    NO_ERROR) {
563                break;
564            }
565        }
566        sAudioTrackCallBackCookies.remove(lpCookie);
567        // delete global refs created in native_setup
568        env->DeleteGlobalRef(lpCookie->audioTrack_class);
569        env->DeleteGlobalRef(lpCookie->audioTrack_ref);
570        delete pJniStorage;
571    }
572}
573
574
575// ----------------------------------------------------------------------------
576static void android_media_AudioTrack_finalize(JNIEnv *env,  jobject thiz) {
577    //ALOGV("android_media_AudioTrack_finalize jobject: %x\n", (int)thiz);
578    android_media_AudioTrack_release(env, thiz);
579}
580
581// overloaded JNI array helper functions (same as in android_media_AudioRecord)
582static inline
583jbyte *envGetArrayElements(JNIEnv *env, jbyteArray array, jboolean *isCopy) {
584    return env->GetByteArrayElements(array, isCopy);
585}
586
587static inline
588void envReleaseArrayElements(JNIEnv *env, jbyteArray array, jbyte *elems, jint mode) {
589    env->ReleaseByteArrayElements(array, elems, mode);
590}
591
592static inline
593jshort *envGetArrayElements(JNIEnv *env, jshortArray array, jboolean *isCopy) {
594    return env->GetShortArrayElements(array, isCopy);
595}
596
597static inline
598void envReleaseArrayElements(JNIEnv *env, jshortArray array, jshort *elems, jint mode) {
599    env->ReleaseShortArrayElements(array, elems, mode);
600}
601
602static inline
603jfloat *envGetArrayElements(JNIEnv *env, jfloatArray array, jboolean *isCopy) {
604    return env->GetFloatArrayElements(array, isCopy);
605}
606
607static inline
608void envReleaseArrayElements(JNIEnv *env, jfloatArray array, jfloat *elems, jint mode) {
609    env->ReleaseFloatArrayElements(array, elems, mode);
610}
611
612static inline
613jint interpretWriteSizeError(ssize_t writeSize) {
614    if (writeSize == WOULD_BLOCK) {
615        return (jint)0;
616    } else if (writeSize == NO_INIT) {
617        return AUDIO_JAVA_DEAD_OBJECT;
618    } else {
619        ALOGE("Error %zd during AudioTrack native read", writeSize);
620        return nativeToJavaStatus(writeSize);
621    }
622}
623
624// ----------------------------------------------------------------------------
625template <typename T>
626static jint writeToTrack(const sp<AudioTrack>& track, jint audioFormat, const T *data,
627                         jint offsetInSamples, jint sizeInSamples, bool blocking) {
628    // give the data to the native AudioTrack object (the data starts at the offset)
629    ssize_t written = 0;
630    // regular write() or copy the data to the AudioTrack's shared memory?
631    size_t sizeInBytes = sizeInSamples * sizeof(T);
632    if (track->sharedBuffer() == 0) {
633        written = track->write(data + offsetInSamples, sizeInBytes, blocking);
634        // for compatibility with earlier behavior of write(), return 0 in this case
635        if (written == (ssize_t) WOULD_BLOCK) {
636            written = 0;
637        }
638    } else {
639        // writing to shared memory, check for capacity
640        if ((size_t)sizeInBytes > track->sharedBuffer()->size()) {
641            sizeInBytes = track->sharedBuffer()->size();
642        }
643        memcpy(track->sharedBuffer()->pointer(), data + offsetInSamples, sizeInBytes);
644        written = sizeInBytes;
645    }
646    if (written >= 0) {
647        return written / sizeof(T);
648    }
649    return interpretWriteSizeError(written);
650}
651
652// ----------------------------------------------------------------------------
653template <typename T>
654static jint android_media_AudioTrack_writeArray(JNIEnv *env, jobject thiz,
655                                                T javaAudioData,
656                                                jint offsetInSamples, jint sizeInSamples,
657                                                jint javaAudioFormat,
658                                                jboolean isWriteBlocking) {
659    //ALOGV("android_media_AudioTrack_writeArray(offset=%d, sizeInSamples=%d) called",
660    //        offsetInSamples, sizeInSamples);
661    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
662    if (lpTrack == NULL) {
663        jniThrowException(env, "java/lang/IllegalStateException",
664            "Unable to retrieve AudioTrack pointer for write()");
665        return (jint)AUDIO_JAVA_INVALID_OPERATION;
666    }
667
668    if (javaAudioData == NULL) {
669        ALOGE("NULL java array of audio data to play");
670        return (jint)AUDIO_JAVA_BAD_VALUE;
671    }
672
673    // NOTE: We may use GetPrimitiveArrayCritical() when the JNI implementation changes in such
674    // a way that it becomes much more efficient. When doing so, we will have to prevent the
675    // AudioSystem callback to be called while in critical section (in case of media server
676    // process crash for instance)
677
678    // get the pointer for the audio data from the java array
679    auto cAudioData = envGetArrayElements(env, javaAudioData, NULL);
680    if (cAudioData == NULL) {
681        ALOGE("Error retrieving source of audio data to play");
682        return (jint)AUDIO_JAVA_BAD_VALUE; // out of memory or no data to load
683    }
684
685    jint samplesWritten = writeToTrack(lpTrack, javaAudioFormat, cAudioData,
686            offsetInSamples, sizeInSamples, isWriteBlocking == JNI_TRUE /* blocking */);
687
688    envReleaseArrayElements(env, javaAudioData, cAudioData, 0);
689
690    //ALOGV("write wrote %d (tried %d) samples in the native AudioTrack with offset %d",
691    //        (int)samplesWritten, (int)(sizeInSamples), (int)offsetInSamples);
692    return samplesWritten;
693}
694
695// ----------------------------------------------------------------------------
696static jint android_media_AudioTrack_write_native_bytes(JNIEnv *env,  jobject thiz,
697        jbyteArray javaBytes, jint byteOffset, jint sizeInBytes,
698        jint javaAudioFormat, jboolean isWriteBlocking) {
699    //ALOGV("android_media_AudioTrack_write_native_bytes(offset=%d, sizeInBytes=%d) called",
700    //    offsetInBytes, sizeInBytes);
701    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
702    if (lpTrack == NULL) {
703        jniThrowException(env, "java/lang/IllegalStateException",
704                "Unable to retrieve AudioTrack pointer for write()");
705        return (jint)AUDIO_JAVA_INVALID_OPERATION;
706    }
707
708    ScopedBytesRO bytes(env, javaBytes);
709    if (bytes.get() == NULL) {
710        ALOGE("Error retrieving source of audio data to play, can't play");
711        return (jint)AUDIO_JAVA_BAD_VALUE;
712    }
713
714    jint written = writeToTrack(lpTrack, javaAudioFormat, bytes.get(), byteOffset,
715            sizeInBytes, isWriteBlocking == JNI_TRUE /* blocking */);
716
717    return written;
718}
719
720// ----------------------------------------------------------------------------
721static jint android_media_AudioTrack_get_buffer_size_frames(JNIEnv *env,  jobject thiz) {
722    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
723    if (lpTrack == NULL) {
724        jniThrowException(env, "java/lang/IllegalStateException",
725            "Unable to retrieve AudioTrack pointer for getBufferSizeInFrames()");
726        return (jint)AUDIO_JAVA_ERROR;
727    }
728
729    ssize_t result = lpTrack->getBufferSizeInFrames();
730    if (result < 0) {
731        jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
732            "Internal error detected in getBufferSizeInFrames() = %zd", result);
733        return (jint)AUDIO_JAVA_ERROR;
734    }
735    return (jint)result;
736}
737
738// ----------------------------------------------------------------------------
739static jint android_media_AudioTrack_set_buffer_size_frames(JNIEnv *env,
740        jobject thiz, jint bufferSizeInFrames) {
741    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
742    if (lpTrack == NULL) {
743        jniThrowException(env, "java/lang/IllegalStateException",
744            "Unable to retrieve AudioTrack pointer for setBufferSizeInFrames()");
745        return (jint)AUDIO_JAVA_ERROR;
746    }
747    // Value will be coerced into the valid range.
748    // But internal values are unsigned, size_t, so we need to clip
749    // against zero here where it is signed.
750    if (bufferSizeInFrames < 0) {
751        bufferSizeInFrames = 0;
752    }
753    ssize_t result = lpTrack->setBufferSizeInFrames(bufferSizeInFrames);
754    if (result < 0) {
755        jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
756            "Internal error detected in setBufferSizeInFrames() = %zd", result);
757        return (jint)AUDIO_JAVA_ERROR;
758    }
759    return (jint)result;
760}
761
762// ----------------------------------------------------------------------------
763static jint android_media_AudioTrack_get_buffer_capacity_frames(JNIEnv *env,  jobject thiz) {
764    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
765    if (lpTrack == NULL) {
766        jniThrowException(env, "java/lang/IllegalStateException",
767            "Unable to retrieve AudioTrack pointer for getBufferCapacityInFrames()");
768        return (jint)AUDIO_JAVA_ERROR;
769    }
770
771    return lpTrack->frameCount();
772}
773
774// ----------------------------------------------------------------------------
775static jint android_media_AudioTrack_set_playback_rate(JNIEnv *env,  jobject thiz,
776        jint sampleRateInHz) {
777    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
778    if (lpTrack == NULL) {
779        jniThrowException(env, "java/lang/IllegalStateException",
780            "Unable to retrieve AudioTrack pointer for setSampleRate()");
781        return (jint)AUDIO_JAVA_ERROR;
782    }
783    return nativeToJavaStatus(lpTrack->setSampleRate(sampleRateInHz));
784}
785
786
787// ----------------------------------------------------------------------------
788static jint android_media_AudioTrack_get_playback_rate(JNIEnv *env,  jobject thiz) {
789    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
790    if (lpTrack == NULL) {
791        jniThrowException(env, "java/lang/IllegalStateException",
792            "Unable to retrieve AudioTrack pointer for getSampleRate()");
793        return (jint)AUDIO_JAVA_ERROR;
794    }
795    return (jint) lpTrack->getSampleRate();
796}
797
798
799// ----------------------------------------------------------------------------
800static void android_media_AudioTrack_set_playback_params(JNIEnv *env,  jobject thiz,
801        jobject params) {
802    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
803    if (lpTrack == NULL) {
804        jniThrowException(env, "java/lang/IllegalStateException",
805            "AudioTrack not initialized");
806        return;
807    }
808
809    PlaybackParams pbp;
810    pbp.fillFromJobject(env, gPlaybackParamsFields, params);
811
812    ALOGV("setPlaybackParams: %d:%f %d:%f %d:%u %d:%u",
813            pbp.speedSet, pbp.audioRate.mSpeed,
814            pbp.pitchSet, pbp.audioRate.mPitch,
815            pbp.audioFallbackModeSet, pbp.audioRate.mFallbackMode,
816            pbp.audioStretchModeSet, pbp.audioRate.mStretchMode);
817
818    // to simulate partially set params, we do a read-modify-write.
819    // TODO: pass in the valid set mask into AudioTrack.
820    AudioPlaybackRate rate = lpTrack->getPlaybackRate();
821    bool updatedRate = false;
822    if (pbp.speedSet) {
823        rate.mSpeed = pbp.audioRate.mSpeed;
824        updatedRate = true;
825    }
826    if (pbp.pitchSet) {
827        rate.mPitch = pbp.audioRate.mPitch;
828        updatedRate = true;
829    }
830    if (pbp.audioFallbackModeSet) {
831        rate.mFallbackMode = pbp.audioRate.mFallbackMode;
832        updatedRate = true;
833    }
834    if (pbp.audioStretchModeSet) {
835        rate.mStretchMode = pbp.audioRate.mStretchMode;
836        updatedRate = true;
837    }
838    if (updatedRate) {
839        if (lpTrack->setPlaybackRate(rate) != OK) {
840            jniThrowException(env, "java/lang/IllegalArgumentException",
841                    "arguments out of range");
842        }
843    }
844}
845
846
847// ----------------------------------------------------------------------------
848static jobject android_media_AudioTrack_get_playback_params(JNIEnv *env,  jobject thiz,
849        jobject params) {
850    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
851    if (lpTrack == NULL) {
852        jniThrowException(env, "java/lang/IllegalStateException",
853            "AudioTrack not initialized");
854        return NULL;
855    }
856
857    PlaybackParams pbs;
858    pbs.audioRate = lpTrack->getPlaybackRate();
859    pbs.speedSet = true;
860    pbs.pitchSet = true;
861    pbs.audioFallbackModeSet = true;
862    pbs.audioStretchModeSet = true;
863    return pbs.asJobject(env, gPlaybackParamsFields);
864}
865
866
867// ----------------------------------------------------------------------------
868static jint android_media_AudioTrack_set_marker_pos(JNIEnv *env,  jobject thiz,
869        jint markerPos) {
870    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
871    if (lpTrack == NULL) {
872        jniThrowException(env, "java/lang/IllegalStateException",
873            "Unable to retrieve AudioTrack pointer for setMarkerPosition()");
874        return (jint)AUDIO_JAVA_ERROR;
875    }
876    return nativeToJavaStatus( lpTrack->setMarkerPosition(markerPos) );
877}
878
879
880// ----------------------------------------------------------------------------
881static jint android_media_AudioTrack_get_marker_pos(JNIEnv *env,  jobject thiz) {
882    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
883    uint32_t markerPos = 0;
884
885    if (lpTrack == NULL) {
886        jniThrowException(env, "java/lang/IllegalStateException",
887            "Unable to retrieve AudioTrack pointer for getMarkerPosition()");
888        return (jint)AUDIO_JAVA_ERROR;
889    }
890    lpTrack->getMarkerPosition(&markerPos);
891    return (jint)markerPos;
892}
893
894
895// ----------------------------------------------------------------------------
896static jint android_media_AudioTrack_set_pos_update_period(JNIEnv *env,  jobject thiz,
897        jint period) {
898    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
899    if (lpTrack == NULL) {
900        jniThrowException(env, "java/lang/IllegalStateException",
901            "Unable to retrieve AudioTrack pointer for setPositionUpdatePeriod()");
902        return (jint)AUDIO_JAVA_ERROR;
903    }
904    return nativeToJavaStatus( lpTrack->setPositionUpdatePeriod(period) );
905}
906
907
908// ----------------------------------------------------------------------------
909static jint android_media_AudioTrack_get_pos_update_period(JNIEnv *env,  jobject thiz) {
910    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
911    uint32_t period = 0;
912
913    if (lpTrack == NULL) {
914        jniThrowException(env, "java/lang/IllegalStateException",
915            "Unable to retrieve AudioTrack pointer for getPositionUpdatePeriod()");
916        return (jint)AUDIO_JAVA_ERROR;
917    }
918    lpTrack->getPositionUpdatePeriod(&period);
919    return (jint)period;
920}
921
922
923// ----------------------------------------------------------------------------
924static jint android_media_AudioTrack_set_position(JNIEnv *env,  jobject thiz,
925        jint position) {
926    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
927    if (lpTrack == NULL) {
928        jniThrowException(env, "java/lang/IllegalStateException",
929            "Unable to retrieve AudioTrack pointer for setPosition()");
930        return (jint)AUDIO_JAVA_ERROR;
931    }
932    return nativeToJavaStatus( lpTrack->setPosition(position) );
933}
934
935
936// ----------------------------------------------------------------------------
937static jint android_media_AudioTrack_get_position(JNIEnv *env,  jobject thiz) {
938    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
939    uint32_t position = 0;
940
941    if (lpTrack == NULL) {
942        jniThrowException(env, "java/lang/IllegalStateException",
943            "Unable to retrieve AudioTrack pointer for getPosition()");
944        return (jint)AUDIO_JAVA_ERROR;
945    }
946    lpTrack->getPosition(&position);
947    return (jint)position;
948}
949
950
951// ----------------------------------------------------------------------------
952static jint android_media_AudioTrack_get_latency(JNIEnv *env,  jobject thiz) {
953    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
954
955    if (lpTrack == NULL) {
956        jniThrowException(env, "java/lang/IllegalStateException",
957            "Unable to retrieve AudioTrack pointer for latency()");
958        return (jint)AUDIO_JAVA_ERROR;
959    }
960    return (jint)lpTrack->latency();
961}
962
963// ----------------------------------------------------------------------------
964static jint android_media_AudioTrack_get_underrun_count(JNIEnv *env,  jobject thiz) {
965    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
966
967    if (lpTrack == NULL) {
968        jniThrowException(env, "java/lang/IllegalStateException",
969            "Unable to retrieve AudioTrack pointer for getUnderrunCount()");
970        return (jint)AUDIO_JAVA_ERROR;
971    }
972    return (jint)lpTrack->getUnderrunCount();
973}
974
975// ----------------------------------------------------------------------------
976static jint android_media_AudioTrack_get_flags(JNIEnv *env,  jobject thiz) {
977    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
978
979    if (lpTrack == NULL) {
980        jniThrowException(env, "java/lang/IllegalStateException",
981            "Unable to retrieve AudioTrack pointer for getFlags()");
982        return (jint)AUDIO_JAVA_ERROR;
983    }
984    return (jint)lpTrack->getFlags();
985}
986
987// ----------------------------------------------------------------------------
988static jint android_media_AudioTrack_get_timestamp(JNIEnv *env,  jobject thiz, jlongArray jTimestamp) {
989    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
990
991    if (lpTrack == NULL) {
992        ALOGE("Unable to retrieve AudioTrack pointer for getTimestamp()");
993        return (jint)AUDIO_JAVA_ERROR;
994    }
995    AudioTimestamp timestamp;
996    status_t status = lpTrack->getTimestamp(timestamp);
997    if (status == OK) {
998        jlong* nTimestamp = (jlong *) env->GetPrimitiveArrayCritical(jTimestamp, NULL);
999        if (nTimestamp == NULL) {
1000            ALOGE("Unable to get array for getTimestamp()");
1001            return (jint)AUDIO_JAVA_ERROR;
1002        }
1003        nTimestamp[0] = (jlong) timestamp.mPosition;
1004        nTimestamp[1] = (jlong) ((timestamp.mTime.tv_sec * 1000000000LL) + timestamp.mTime.tv_nsec);
1005        env->ReleasePrimitiveArrayCritical(jTimestamp, nTimestamp, 0);
1006    }
1007    return (jint) nativeToJavaStatus(status);
1008}
1009
1010
1011// ----------------------------------------------------------------------------
1012static jint android_media_AudioTrack_set_loop(JNIEnv *env,  jobject thiz,
1013        jint loopStart, jint loopEnd, jint loopCount) {
1014    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1015    if (lpTrack == NULL) {
1016        jniThrowException(env, "java/lang/IllegalStateException",
1017            "Unable to retrieve AudioTrack pointer for setLoop()");
1018        return (jint)AUDIO_JAVA_ERROR;
1019    }
1020    return nativeToJavaStatus( lpTrack->setLoop(loopStart, loopEnd, loopCount) );
1021}
1022
1023
1024// ----------------------------------------------------------------------------
1025static jint android_media_AudioTrack_reload(JNIEnv *env,  jobject thiz) {
1026    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1027    if (lpTrack == NULL) {
1028        jniThrowException(env, "java/lang/IllegalStateException",
1029            "Unable to retrieve AudioTrack pointer for reload()");
1030        return (jint)AUDIO_JAVA_ERROR;
1031    }
1032    return nativeToJavaStatus( lpTrack->reload() );
1033}
1034
1035
1036// ----------------------------------------------------------------------------
1037static jint android_media_AudioTrack_get_output_sample_rate(JNIEnv *env,  jobject thiz,
1038        jint javaStreamType) {
1039    uint32_t afSamplingRate;
1040    // convert the stream type from Java to native value
1041    // FIXME: code duplication with android_media_AudioTrack_setup()
1042    audio_stream_type_t nativeStreamType;
1043    switch (javaStreamType) {
1044    case AUDIO_STREAM_VOICE_CALL:
1045    case AUDIO_STREAM_SYSTEM:
1046    case AUDIO_STREAM_RING:
1047    case AUDIO_STREAM_MUSIC:
1048    case AUDIO_STREAM_ALARM:
1049    case AUDIO_STREAM_NOTIFICATION:
1050    case AUDIO_STREAM_BLUETOOTH_SCO:
1051    case AUDIO_STREAM_DTMF:
1052        nativeStreamType = (audio_stream_type_t) javaStreamType;
1053        break;
1054    default:
1055        nativeStreamType = AUDIO_STREAM_DEFAULT;
1056        break;
1057    }
1058
1059    status_t status = AudioSystem::getOutputSamplingRate(&afSamplingRate, nativeStreamType);
1060    if (status != NO_ERROR) {
1061        ALOGE("Error %d in AudioSystem::getOutputSamplingRate() for stream type %d "
1062              "in AudioTrack JNI", status, nativeStreamType);
1063        return DEFAULT_OUTPUT_SAMPLE_RATE;
1064    } else {
1065        return afSamplingRate;
1066    }
1067}
1068
1069
1070// ----------------------------------------------------------------------------
1071// returns the minimum required size for the successful creation of a streaming AudioTrack
1072// returns -1 if there was an error querying the hardware.
1073static jint android_media_AudioTrack_get_min_buff_size(JNIEnv *env,  jobject thiz,
1074    jint sampleRateInHertz, jint channelCount, jint audioFormat) {
1075
1076    size_t frameCount;
1077    const status_t status = AudioTrack::getMinFrameCount(&frameCount, AUDIO_STREAM_DEFAULT,
1078            sampleRateInHertz);
1079    if (status != NO_ERROR) {
1080        ALOGE("AudioTrack::getMinFrameCount() for sample rate %d failed with status %d",
1081                sampleRateInHertz, status);
1082        return -1;
1083    }
1084    const audio_format_t format = audioFormatToNative(audioFormat);
1085    if (audio_has_proportional_frames(format)) {
1086        const size_t bytesPerSample = audio_bytes_per_sample(format);
1087        return frameCount * channelCount * bytesPerSample;
1088    } else {
1089        return frameCount;
1090    }
1091}
1092
1093// ----------------------------------------------------------------------------
1094static jint
1095android_media_AudioTrack_setAuxEffectSendLevel(JNIEnv *env, jobject thiz, jfloat level )
1096{
1097    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1098    if (lpTrack == NULL ) {
1099        jniThrowException(env, "java/lang/IllegalStateException",
1100            "Unable to retrieve AudioTrack pointer for setAuxEffectSendLevel()");
1101        return -1;
1102    }
1103
1104    status_t status = lpTrack->setAuxEffectSendLevel(level);
1105    if (status != NO_ERROR) {
1106        ALOGE("AudioTrack::setAuxEffectSendLevel() for level %g failed with status %d",
1107                level, status);
1108    }
1109    return (jint) status;
1110}
1111
1112// ----------------------------------------------------------------------------
1113static jint android_media_AudioTrack_attachAuxEffect(JNIEnv *env,  jobject thiz,
1114        jint effectId) {
1115    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1116    if (lpTrack == NULL) {
1117        jniThrowException(env, "java/lang/IllegalStateException",
1118            "Unable to retrieve AudioTrack pointer for attachAuxEffect()");
1119        return (jint)AUDIO_JAVA_ERROR;
1120    }
1121    return nativeToJavaStatus( lpTrack->attachAuxEffect(effectId) );
1122}
1123
1124static jboolean android_media_AudioTrack_setOutputDevice(
1125                JNIEnv *env,  jobject thiz, jint device_id) {
1126
1127    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1128    if (lpTrack == 0) {
1129        return false;
1130    }
1131    return lpTrack->setOutputDevice(device_id) == NO_ERROR;
1132}
1133
1134static jint android_media_AudioTrack_getRoutedDeviceId(
1135                JNIEnv *env,  jobject thiz) {
1136
1137    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1138    if (lpTrack == NULL) {
1139        return 0;
1140    }
1141    return (jint)lpTrack->getRoutedDeviceId();
1142}
1143
1144static void android_media_AudioTrack_enableDeviceCallback(
1145                JNIEnv *env,  jobject thiz) {
1146
1147    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1148    if (lpTrack == NULL) {
1149        return;
1150    }
1151    AudioTrackJniStorage* pJniStorage = (AudioTrackJniStorage *)env->GetLongField(
1152        thiz, javaAudioTrackFields.jniData);
1153    if (pJniStorage == NULL || pJniStorage->mDeviceCallback != 0) {
1154        return;
1155    }
1156    pJniStorage->mDeviceCallback =
1157    new JNIDeviceCallback(env, thiz, pJniStorage->mCallbackData.audioTrack_ref,
1158                          javaAudioTrackFields.postNativeEventInJava);
1159    lpTrack->addAudioDeviceCallback(pJniStorage->mDeviceCallback);
1160}
1161
1162static void android_media_AudioTrack_disableDeviceCallback(
1163                JNIEnv *env,  jobject thiz) {
1164
1165    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1166    if (lpTrack == NULL) {
1167        return;
1168    }
1169    AudioTrackJniStorage* pJniStorage = (AudioTrackJniStorage *)env->GetLongField(
1170        thiz, javaAudioTrackFields.jniData);
1171    if (pJniStorage == NULL || pJniStorage->mDeviceCallback == 0) {
1172        return;
1173    }
1174    lpTrack->removeAudioDeviceCallback(pJniStorage->mDeviceCallback);
1175    pJniStorage->mDeviceCallback.clear();
1176}
1177
1178static jint android_media_AudioTrack_get_FCC_8(JNIEnv *env, jobject thiz) {
1179    return FCC_8;
1180}
1181
1182// Pass through the arguments to the AudioFlinger track implementation.
1183static jint android_media_AudioTrack_apply_volume_shaper(JNIEnv *env, jobject thiz,
1184        jobject jconfig, jobject joperation) {
1185    // NOTE: hard code here to prevent platform issues. Must match VolumeShaper.java
1186    const int VOLUME_SHAPER_INVALID_OPERATION = -38;
1187
1188    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1189    if (lpTrack == nullptr) {
1190        return (jint)VOLUME_SHAPER_INVALID_OPERATION;
1191    }
1192
1193    sp<VolumeShaper::Configuration> configuration;
1194    sp<VolumeShaper::Operation> operation;
1195    if (jconfig != nullptr) {
1196        configuration = VolumeShaperHelper::convertJobjectToConfiguration(
1197                env, gVolumeShaperFields, jconfig);
1198        ALOGV("applyVolumeShaper configuration: %s", configuration->toString().c_str());
1199    }
1200    if (joperation != nullptr) {
1201        operation = VolumeShaperHelper::convertJobjectToOperation(
1202                env, gVolumeShaperFields, joperation);
1203        ALOGV("applyVolumeShaper operation: %s", operation->toString().c_str());
1204    }
1205    VolumeShaper::Status status = lpTrack->applyVolumeShaper(configuration, operation);
1206    if (status == INVALID_OPERATION) {
1207        status = VOLUME_SHAPER_INVALID_OPERATION;
1208    }
1209    return (jint)status; // if status < 0 an error, else a VolumeShaper id
1210}
1211
1212// Pass through the arguments to the AudioFlinger track implementation.
1213static jobject android_media_AudioTrack_get_volume_shaper_state(JNIEnv *env, jobject thiz,
1214        jint id) {
1215    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
1216    if (lpTrack == nullptr) {
1217        return (jobject)nullptr;
1218    }
1219
1220    sp<VolumeShaper::State> state = lpTrack->getVolumeShaperState((int)id);
1221    if (state.get() == nullptr) {
1222        return (jobject)nullptr;
1223    }
1224    return VolumeShaperHelper::convertStateToJobject(env, gVolumeShaperFields, state);
1225}
1226
1227// ----------------------------------------------------------------------------
1228// ----------------------------------------------------------------------------
1229static const JNINativeMethod gMethods[] = {
1230    // name,              signature,     funcPtr
1231    {"native_start",         "()V",      (void *)android_media_AudioTrack_start},
1232    {"native_stop",          "()V",      (void *)android_media_AudioTrack_stop},
1233    {"native_pause",         "()V",      (void *)android_media_AudioTrack_pause},
1234    {"native_flush",         "()V",      (void *)android_media_AudioTrack_flush},
1235    {"native_setup",     "(Ljava/lang/Object;Ljava/lang/Object;[IIIIII[IJ)I",
1236                                         (void *)android_media_AudioTrack_setup},
1237    {"native_finalize",      "()V",      (void *)android_media_AudioTrack_finalize},
1238    {"native_release",       "()V",      (void *)android_media_AudioTrack_release},
1239    {"native_write_byte",    "([BIIIZ)I",(void *)android_media_AudioTrack_writeArray<jbyteArray>},
1240    {"native_write_native_bytes",
1241                             "(Ljava/lang/Object;IIIZ)I",
1242                                         (void *)android_media_AudioTrack_write_native_bytes},
1243    {"native_write_short",   "([SIIIZ)I",(void *)android_media_AudioTrack_writeArray<jshortArray>},
1244    {"native_write_float",   "([FIIIZ)I",(void *)android_media_AudioTrack_writeArray<jfloatArray>},
1245    {"native_setVolume",     "(FF)V",    (void *)android_media_AudioTrack_set_volume},
1246    {"native_get_buffer_size_frames",
1247                             "()I",      (void *)android_media_AudioTrack_get_buffer_size_frames},
1248    {"native_set_buffer_size_frames",
1249                             "(I)I",     (void *)android_media_AudioTrack_set_buffer_size_frames},
1250    {"native_get_buffer_capacity_frames",
1251                             "()I",      (void *)android_media_AudioTrack_get_buffer_capacity_frames},
1252    {"native_set_playback_rate",
1253                             "(I)I",     (void *)android_media_AudioTrack_set_playback_rate},
1254    {"native_get_playback_rate",
1255                             "()I",      (void *)android_media_AudioTrack_get_playback_rate},
1256    {"native_set_playback_params",
1257                             "(Landroid/media/PlaybackParams;)V",
1258                                         (void *)android_media_AudioTrack_set_playback_params},
1259    {"native_get_playback_params",
1260                             "()Landroid/media/PlaybackParams;",
1261                                         (void *)android_media_AudioTrack_get_playback_params},
1262    {"native_set_marker_pos","(I)I",     (void *)android_media_AudioTrack_set_marker_pos},
1263    {"native_get_marker_pos","()I",      (void *)android_media_AudioTrack_get_marker_pos},
1264    {"native_set_pos_update_period",
1265                             "(I)I",     (void *)android_media_AudioTrack_set_pos_update_period},
1266    {"native_get_pos_update_period",
1267                             "()I",      (void *)android_media_AudioTrack_get_pos_update_period},
1268    {"native_set_position",  "(I)I",     (void *)android_media_AudioTrack_set_position},
1269    {"native_get_position",  "()I",      (void *)android_media_AudioTrack_get_position},
1270    {"native_get_latency",   "()I",      (void *)android_media_AudioTrack_get_latency},
1271    {"native_get_underrun_count", "()I",      (void *)android_media_AudioTrack_get_underrun_count},
1272    {"native_get_flags",     "()I",      (void *)android_media_AudioTrack_get_flags},
1273    {"native_get_timestamp", "([J)I",    (void *)android_media_AudioTrack_get_timestamp},
1274    {"native_set_loop",      "(III)I",   (void *)android_media_AudioTrack_set_loop},
1275    {"native_reload_static", "()I",      (void *)android_media_AudioTrack_reload},
1276    {"native_get_output_sample_rate",
1277                             "(I)I",      (void *)android_media_AudioTrack_get_output_sample_rate},
1278    {"native_get_min_buff_size",
1279                             "(III)I",   (void *)android_media_AudioTrack_get_min_buff_size},
1280    {"native_setAuxEffectSendLevel",
1281                             "(F)I",     (void *)android_media_AudioTrack_setAuxEffectSendLevel},
1282    {"native_attachAuxEffect",
1283                             "(I)I",     (void *)android_media_AudioTrack_attachAuxEffect},
1284    {"native_setOutputDevice", "(I)Z",
1285                             (void *)android_media_AudioTrack_setOutputDevice},
1286    {"native_getRoutedDeviceId", "()I", (void *)android_media_AudioTrack_getRoutedDeviceId},
1287    {"native_enableDeviceCallback", "()V", (void *)android_media_AudioTrack_enableDeviceCallback},
1288    {"native_disableDeviceCallback", "()V", (void *)android_media_AudioTrack_disableDeviceCallback},
1289    {"native_get_FCC_8",     "()I",      (void *)android_media_AudioTrack_get_FCC_8},
1290    {"native_applyVolumeShaper",
1291            "(Landroid/media/VolumeShaper$Configuration;Landroid/media/VolumeShaper$Operation;)I",
1292                                         (void *)android_media_AudioTrack_apply_volume_shaper},
1293    {"native_getVolumeShaperState",
1294            "(I)Landroid/media/VolumeShaper$State;",
1295                                        (void *)android_media_AudioTrack_get_volume_shaper_state},
1296};
1297
1298
1299// field names found in android/media/AudioTrack.java
1300#define JAVA_POSTEVENT_CALLBACK_NAME                    "postEventFromNative"
1301#define JAVA_NATIVETRACKINJAVAOBJ_FIELD_NAME            "mNativeTrackInJavaObj"
1302#define JAVA_JNIDATA_FIELD_NAME                         "mJniData"
1303#define JAVA_STREAMTYPE_FIELD_NAME                      "mStreamType"
1304
1305// ----------------------------------------------------------------------------
1306// preconditions:
1307//    theClass is valid
1308bool android_media_getIntConstantFromClass(JNIEnv* pEnv, jclass theClass, const char* className,
1309                             const char* constName, int* constVal) {
1310    jfieldID javaConst = NULL;
1311    javaConst = pEnv->GetStaticFieldID(theClass, constName, "I");
1312    if (javaConst != NULL) {
1313        *constVal = pEnv->GetStaticIntField(theClass, javaConst);
1314        return true;
1315    } else {
1316        ALOGE("Can't find %s.%s", className, constName);
1317        return false;
1318    }
1319}
1320
1321
1322// ----------------------------------------------------------------------------
1323int register_android_media_AudioTrack(JNIEnv *env)
1324{
1325    // must be first
1326    int res = RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
1327
1328    javaAudioTrackFields.nativeTrackInJavaObj = NULL;
1329    javaAudioTrackFields.postNativeEventInJava = NULL;
1330
1331    // Get the AudioTrack class
1332    jclass audioTrackClass = FindClassOrDie(env, kClassPathName);
1333
1334    // Get the postEvent method
1335    javaAudioTrackFields.postNativeEventInJava = GetStaticMethodIDOrDie(env,
1336            audioTrackClass, JAVA_POSTEVENT_CALLBACK_NAME,
1337            "(Ljava/lang/Object;IIILjava/lang/Object;)V");
1338
1339    // Get the variables fields
1340    //      nativeTrackInJavaObj
1341    javaAudioTrackFields.nativeTrackInJavaObj = GetFieldIDOrDie(env,
1342            audioTrackClass, JAVA_NATIVETRACKINJAVAOBJ_FIELD_NAME, "J");
1343    //      jniData
1344    javaAudioTrackFields.jniData = GetFieldIDOrDie(env,
1345            audioTrackClass, JAVA_JNIDATA_FIELD_NAME, "J");
1346    //      fieldStreamType
1347    javaAudioTrackFields.fieldStreamType = GetFieldIDOrDie(env,
1348            audioTrackClass, JAVA_STREAMTYPE_FIELD_NAME, "I");
1349
1350    env->DeleteLocalRef(audioTrackClass);
1351
1352    // Get the AudioAttributes class and fields
1353    jclass audioAttrClass = FindClassOrDie(env, kAudioAttributesClassPathName);
1354    javaAudioAttrFields.fieldUsage = GetFieldIDOrDie(env, audioAttrClass, "mUsage", "I");
1355    javaAudioAttrFields.fieldContentType = GetFieldIDOrDie(env,
1356            audioAttrClass, "mContentType", "I");
1357    javaAudioAttrFields.fieldFlags = GetFieldIDOrDie(env, audioAttrClass, "mFlags", "I");
1358    javaAudioAttrFields.fieldFormattedTags = GetFieldIDOrDie(env,
1359            audioAttrClass, "mFormattedTags", "Ljava/lang/String;");
1360
1361    env->DeleteLocalRef(audioAttrClass);
1362
1363    // initialize PlaybackParams field info
1364    gPlaybackParamsFields.init(env);
1365
1366    gVolumeShaperFields.init(env);
1367    return res;
1368}
1369
1370
1371// ----------------------------------------------------------------------------
1372