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