1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <stdio.h>
18
19//#define LOG_NDEBUG 0
20#define LOG_TAG "AudioEffects-JNI"
21
22#include <utils/Log.h>
23#include <nativehelper/jni.h>
24#include <nativehelper/JNIHelp.h>
25#include <android_runtime/AndroidRuntime.h>
26#include "media/AudioEffect.h"
27
28#include <ScopedUtfChars.h>
29
30using namespace android;
31
32#define AUDIOEFFECT_SUCCESS                      0
33#define AUDIOEFFECT_ERROR                       -1
34#define AUDIOEFFECT_ERROR_ALREADY_EXISTS        -2
35#define AUDIOEFFECT_ERROR_NO_INIT               -3
36#define AUDIOEFFECT_ERROR_BAD_VALUE             -4
37#define AUDIOEFFECT_ERROR_INVALID_OPERATION     -5
38#define AUDIOEFFECT_ERROR_NO_MEMORY             -6
39#define AUDIOEFFECT_ERROR_DEAD_OBJECT           -7
40
41// ----------------------------------------------------------------------------
42static const char* const kClassPathName = "android/media/audiofx/AudioEffect";
43
44struct fields_t {
45    // these fields provide access from C++ to the...
46    jclass    clazzEffect;          // AudioEffect class
47    jmethodID midPostNativeEvent;   // event post callback method
48    jfieldID  fidNativeAudioEffect; // stores in Java the native AudioEffect object
49    jfieldID  fidJniData;           // stores in Java additional resources used by the native AudioEffect
50    jclass    clazzDesc;            // AudioEffect.Descriptor class
51    jmethodID midDescCstor;         // AudioEffect.Descriptor class constructor
52};
53static fields_t fields;
54
55struct effect_callback_cookie {
56    jclass      audioEffect_class;  // AudioEffect class
57    jobject     audioEffect_ref;    // AudioEffect object instance
58 };
59
60// ----------------------------------------------------------------------------
61class AudioEffectJniStorage {
62    public:
63        effect_callback_cookie mCallbackData;
64
65    AudioEffectJniStorage() {
66    }
67
68    ~AudioEffectJniStorage() {
69    }
70
71};
72
73
74static jint translateError(int code) {
75    switch(code) {
76    case NO_ERROR:
77        return AUDIOEFFECT_SUCCESS;
78    case ALREADY_EXISTS:
79        return AUDIOEFFECT_ERROR_ALREADY_EXISTS;
80    case NO_INIT:
81        return AUDIOEFFECT_ERROR_NO_INIT;
82    case BAD_VALUE:
83        return AUDIOEFFECT_ERROR_BAD_VALUE;
84    case INVALID_OPERATION:
85        return AUDIOEFFECT_ERROR_INVALID_OPERATION;
86    case NO_MEMORY:
87        return AUDIOEFFECT_ERROR_NO_MEMORY;
88    case DEAD_OBJECT:
89        return AUDIOEFFECT_ERROR_DEAD_OBJECT;
90    default:
91        return AUDIOEFFECT_ERROR;
92    }
93}
94
95static Mutex sLock;
96
97// ----------------------------------------------------------------------------
98static void effectCallback(int event, void* user, void *info) {
99
100    effect_param_t *p;
101    int arg1 = 0;
102    int arg2 = 0;
103    jobject obj = NULL;
104    jbyteArray array = NULL;
105    jbyte *bytes;
106    bool param;
107    size_t size;
108
109    effect_callback_cookie *callbackInfo = (effect_callback_cookie *)user;
110    JNIEnv *env = AndroidRuntime::getJNIEnv();
111
112    ALOGV("effectCallback: callbackInfo %p, audioEffect_ref %p audioEffect_class %p",
113            callbackInfo,
114            callbackInfo->audioEffect_ref,
115            callbackInfo->audioEffect_class);
116
117    if (!user || !env) {
118        ALOGW("effectCallback error user %p, env %p", user, env);
119        return;
120    }
121
122    switch (event) {
123    case AudioEffect::EVENT_CONTROL_STATUS_CHANGED:
124        if (info == 0) {
125            ALOGW("EVENT_CONTROL_STATUS_CHANGED info == NULL");
126            goto effectCallback_Exit;
127        }
128        param = *(bool *)info;
129        arg1 = (int)param;
130        ALOGV("EVENT_CONTROL_STATUS_CHANGED");
131        break;
132    case AudioEffect::EVENT_ENABLE_STATUS_CHANGED:
133        if (info == 0) {
134            ALOGW("EVENT_ENABLE_STATUS_CHANGED info == NULL");
135            goto effectCallback_Exit;
136        }
137        param = *(bool *)info;
138        arg1 = (int)param;
139        ALOGV("EVENT_ENABLE_STATUS_CHANGED");
140        break;
141    case AudioEffect::EVENT_PARAMETER_CHANGED:
142        if (info == 0) {
143            ALOGW("EVENT_PARAMETER_CHANGED info == NULL");
144            goto effectCallback_Exit;
145        }
146        p = (effect_param_t *)info;
147        if (p->psize == 0 || p->vsize == 0) {
148            goto effectCallback_Exit;
149        }
150        // arg1 contains offset of parameter value from start of byte array
151        arg1 = sizeof(effect_param_t) + ((p->psize - 1) / sizeof(int) + 1) * sizeof(int);
152        size = arg1 + p->vsize;
153        array = env->NewByteArray(size);
154        if (array == NULL) {
155            ALOGE("effectCallback: Couldn't allocate byte array for parameter data");
156            goto effectCallback_Exit;
157        }
158        bytes = env->GetByteArrayElements(array, NULL);
159        memcpy(bytes, p, size);
160        env->ReleaseByteArrayElements(array, bytes, 0);
161        obj = array;
162        ALOGV("EVENT_PARAMETER_CHANGED");
163       break;
164    case AudioEffect::EVENT_ERROR:
165        ALOGW("EVENT_ERROR");
166        break;
167    }
168
169    env->CallStaticVoidMethod(
170        callbackInfo->audioEffect_class,
171        fields.midPostNativeEvent,
172        callbackInfo->audioEffect_ref, event, arg1, arg2, obj);
173
174effectCallback_Exit:
175    if (array) {
176        env->DeleteLocalRef(array);
177    }
178
179    if (env->ExceptionCheck()) {
180        env->ExceptionDescribe();
181        env->ExceptionClear();
182    }
183}
184
185// ----------------------------------------------------------------------------
186
187static sp<AudioEffect> getAudioEffect(JNIEnv* env, jobject thiz)
188{
189    Mutex::Autolock l(sLock);
190    AudioEffect* const ae =
191            (AudioEffect*)env->GetLongField(thiz, fields.fidNativeAudioEffect);
192    return sp<AudioEffect>(ae);
193}
194
195static sp<AudioEffect> setAudioEffect(JNIEnv* env, jobject thiz,
196                                    const sp<AudioEffect>& ae)
197{
198    Mutex::Autolock l(sLock);
199    sp<AudioEffect> old =
200            (AudioEffect*)env->GetLongField(thiz, fields.fidNativeAudioEffect);
201    if (ae.get()) {
202        ae->incStrong((void*)setAudioEffect);
203    }
204    if (old != 0) {
205        old->decStrong((void*)setAudioEffect);
206    }
207    env->SetLongField(thiz, fields.fidNativeAudioEffect, (jlong)ae.get());
208    return old;
209}
210
211// ----------------------------------------------------------------------------
212// This function gets some field IDs, which in turn causes class initialization.
213// It is called from a static block in AudioEffect, which won't run until the
214// first time an instance of this class is used.
215static void
216android_media_AudioEffect_native_init(JNIEnv *env)
217{
218
219    ALOGV("android_media_AudioEffect_native_init");
220
221    fields.clazzEffect = NULL;
222    fields.clazzDesc = NULL;
223
224    // Get the AudioEffect class
225    jclass clazz = env->FindClass(kClassPathName);
226    if (clazz == NULL) {
227        ALOGE("Can't find %s", kClassPathName);
228        return;
229    }
230
231    fields.clazzEffect = (jclass)env->NewGlobalRef(clazz);
232
233    // Get the postEvent method
234    fields.midPostNativeEvent = env->GetStaticMethodID(
235            fields.clazzEffect,
236            "postEventFromNative", "(Ljava/lang/Object;IIILjava/lang/Object;)V");
237    if (fields.midPostNativeEvent == NULL) {
238        ALOGE("Can't find AudioEffect.%s", "postEventFromNative");
239        return;
240    }
241
242    // Get the variables fields
243    //      nativeTrackInJavaObj
244    fields.fidNativeAudioEffect = env->GetFieldID(
245            fields.clazzEffect,
246            "mNativeAudioEffect", "J");
247    if (fields.fidNativeAudioEffect == NULL) {
248        ALOGE("Can't find AudioEffect.%s", "mNativeAudioEffect");
249        return;
250    }
251    //      fidJniData;
252    fields.fidJniData = env->GetFieldID(
253            fields.clazzEffect,
254            "mJniData", "J");
255    if (fields.fidJniData == NULL) {
256        ALOGE("Can't find AudioEffect.%s", "mJniData");
257        return;
258    }
259
260    clazz = env->FindClass("android/media/audiofx/AudioEffect$Descriptor");
261    if (clazz == NULL) {
262        ALOGE("Can't find android/media/audiofx/AudioEffect$Descriptor class");
263        return;
264    }
265    fields.clazzDesc = (jclass)env->NewGlobalRef(clazz);
266
267    fields.midDescCstor
268            = env->GetMethodID(
269                    fields.clazzDesc,
270                    "<init>",
271                    "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
272    if (fields.midDescCstor == NULL) {
273        ALOGE("Can't find android/media/audiofx/AudioEffect$Descriptor class constructor");
274        return;
275    }
276}
277
278
279static jint
280android_media_AudioEffect_native_setup(JNIEnv *env, jobject thiz, jobject weak_this,
281        jstring type, jstring uuid, jint priority, jint sessionId, jintArray jId,
282        jobjectArray javadesc, jstring opPackageName)
283{
284    ALOGV("android_media_AudioEffect_native_setup");
285    AudioEffectJniStorage* lpJniStorage = NULL;
286    int lStatus = AUDIOEFFECT_ERROR_NO_MEMORY;
287    sp<AudioEffect> lpAudioEffect;
288    jint* nId = NULL;
289    const char *typeStr = NULL;
290    const char *uuidStr = NULL;
291    effect_descriptor_t desc;
292    jobject jdesc;
293    char str[EFFECT_STRING_LEN_MAX];
294    jstring jdescType;
295    jstring jdescUuid;
296    jstring jdescConnect;
297    jstring jdescName;
298    jstring jdescImplementor;
299
300    ScopedUtfChars opPackageNameStr(env, opPackageName);
301
302    setAudioEffect(env, thiz, 0);
303
304    if (type != NULL) {
305        typeStr = env->GetStringUTFChars(type, NULL);
306        if (typeStr == NULL) {  // Out of memory
307            jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
308            goto setup_failure;
309        }
310    }
311
312    if (uuid != NULL) {
313        uuidStr = env->GetStringUTFChars(uuid, NULL);
314        if (uuidStr == NULL) {  // Out of memory
315            jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
316            goto setup_failure;
317        }
318    }
319
320    if (typeStr == NULL && uuidStr == NULL) {
321        lStatus = AUDIOEFFECT_ERROR_BAD_VALUE;
322        goto setup_failure;
323    }
324
325    lpJniStorage = new AudioEffectJniStorage();
326    if (lpJniStorage == NULL) {
327        ALOGE("setup: Error creating JNI Storage");
328        goto setup_failure;
329    }
330
331    lpJniStorage->mCallbackData.audioEffect_class = (jclass)env->NewGlobalRef(fields.clazzEffect);
332    // we use a weak reference so the AudioEffect object can be garbage collected.
333    lpJniStorage->mCallbackData.audioEffect_ref = env->NewGlobalRef(weak_this);
334
335    ALOGV("setup: lpJniStorage: %p audioEffect_ref %p audioEffect_class %p, &mCallbackData %p",
336            lpJniStorage,
337            lpJniStorage->mCallbackData.audioEffect_ref,
338            lpJniStorage->mCallbackData.audioEffect_class,
339            &lpJniStorage->mCallbackData);
340
341    if (jId == NULL) {
342        ALOGE("setup: NULL java array for id pointer");
343        lStatus = AUDIOEFFECT_ERROR_BAD_VALUE;
344        goto setup_failure;
345    }
346
347    // create the native AudioEffect object
348    lpAudioEffect = new AudioEffect(typeStr,
349                                    String16(opPackageNameStr.c_str()),
350                                    uuidStr,
351                                    priority,
352                                    effectCallback,
353                                    &lpJniStorage->mCallbackData,
354                                    (audio_session_t) sessionId,
355                                    0);
356    if (lpAudioEffect == 0) {
357        ALOGE("Error creating AudioEffect");
358        goto setup_failure;
359    }
360
361    lStatus = translateError(lpAudioEffect->initCheck());
362    if (lStatus != AUDIOEFFECT_SUCCESS && lStatus != AUDIOEFFECT_ERROR_ALREADY_EXISTS) {
363        ALOGE("AudioEffect initCheck failed %d", lStatus);
364        goto setup_failure;
365    }
366
367    nId = (jint *) env->GetPrimitiveArrayCritical(jId, NULL);
368    if (nId == NULL) {
369        ALOGE("setup: Error retrieving id pointer");
370        lStatus = AUDIOEFFECT_ERROR_BAD_VALUE;
371        goto setup_failure;
372    }
373    nId[0] = lpAudioEffect->id();
374    env->ReleasePrimitiveArrayCritical(jId, nId, 0);
375    nId = NULL;
376
377    if (typeStr) {
378        env->ReleaseStringUTFChars(type, typeStr);
379        typeStr = NULL;
380    }
381
382    if (uuidStr) {
383        env->ReleaseStringUTFChars(uuid, uuidStr);
384        uuidStr = NULL;
385    }
386
387    // get the effect descriptor
388    desc = lpAudioEffect->descriptor();
389
390    AudioEffect::guidToString(&desc.type, str, EFFECT_STRING_LEN_MAX);
391    jdescType = env->NewStringUTF(str);
392
393    AudioEffect::guidToString(&desc.uuid, str, EFFECT_STRING_LEN_MAX);
394    jdescUuid = env->NewStringUTF(str);
395
396    if ((desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
397        jdescConnect = env->NewStringUTF("Auxiliary");
398    } else if ((desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_PRE_PROC) {
399        jdescConnect = env->NewStringUTF("Pre Processing");
400    } else {
401        jdescConnect = env->NewStringUTF("Insert");
402    }
403
404    jdescName = env->NewStringUTF(desc.name);
405    jdescImplementor = env->NewStringUTF(desc.implementor);
406
407    jdesc = env->NewObject(fields.clazzDesc,
408                           fields.midDescCstor,
409                           jdescType,
410                           jdescUuid,
411                           jdescConnect,
412                           jdescName,
413                           jdescImplementor);
414    env->DeleteLocalRef(jdescType);
415    env->DeleteLocalRef(jdescUuid);
416    env->DeleteLocalRef(jdescConnect);
417    env->DeleteLocalRef(jdescName);
418    env->DeleteLocalRef(jdescImplementor);
419    if (jdesc == NULL) {
420        ALOGE("env->NewObject(fields.clazzDesc, fields.midDescCstor)");
421        goto setup_failure;
422    }
423
424    env->SetObjectArrayElement(javadesc, 0, jdesc);
425
426    setAudioEffect(env, thiz, lpAudioEffect);
427
428    env->SetLongField(thiz, fields.fidJniData, (jlong)lpJniStorage);
429
430    return (jint) AUDIOEFFECT_SUCCESS;
431
432    // failures:
433setup_failure:
434
435    if (nId != NULL) {
436        env->ReleasePrimitiveArrayCritical(jId, nId, 0);
437    }
438
439    if (lpJniStorage) {
440        env->DeleteGlobalRef(lpJniStorage->mCallbackData.audioEffect_class);
441        env->DeleteGlobalRef(lpJniStorage->mCallbackData.audioEffect_ref);
442        delete lpJniStorage;
443    }
444    env->SetLongField(thiz, fields.fidJniData, 0);
445
446    if (uuidStr != NULL) {
447        env->ReleaseStringUTFChars(uuid, uuidStr);
448    }
449
450    if (typeStr != NULL) {
451        env->ReleaseStringUTFChars(type, typeStr);
452    }
453
454    return (jint)lStatus;
455}
456
457
458// ----------------------------------------------------------------------------
459static void android_media_AudioEffect_native_release(JNIEnv *env,  jobject thiz) {
460    sp<AudioEffect> lpAudioEffect = setAudioEffect(env, thiz, 0);
461    if (lpAudioEffect == 0) {
462        return;
463    }
464
465    // delete the JNI data
466    AudioEffectJniStorage* lpJniStorage =
467        (AudioEffectJniStorage *)env->GetLongField(thiz, fields.fidJniData);
468
469    // reset the native resources in the Java object so any attempt to access
470    // them after a call to release fails.
471    env->SetLongField(thiz, fields.fidJniData, 0);
472
473    if (lpJniStorage) {
474        ALOGV("deleting pJniStorage: %p\n", lpJniStorage);
475        env->DeleteGlobalRef(lpJniStorage->mCallbackData.audioEffect_class);
476        env->DeleteGlobalRef(lpJniStorage->mCallbackData.audioEffect_ref);
477        delete lpJniStorage;
478    }
479}
480
481// ----------------------------------------------------------------------------
482static void android_media_AudioEffect_native_finalize(JNIEnv *env,  jobject thiz) {
483    ALOGV("android_media_AudioEffect_native_finalize jobject: %p\n", thiz);
484    android_media_AudioEffect_native_release(env, thiz);
485}
486
487static jint
488android_media_AudioEffect_native_setEnabled(JNIEnv *env, jobject thiz, jboolean enabled)
489{
490    sp<AudioEffect> lpAudioEffect = getAudioEffect(env, thiz);
491    if (lpAudioEffect == 0) {
492        jniThrowException(env, "java/lang/IllegalStateException",
493            "Unable to retrieve AudioEffect pointer for enable()");
494        return AUDIOEFFECT_ERROR_NO_INIT;
495    }
496
497    return (jint) translateError(lpAudioEffect->setEnabled(enabled));
498}
499
500static jboolean
501android_media_AudioEffect_native_getEnabled(JNIEnv *env, jobject thiz)
502{
503  sp<AudioEffect> lpAudioEffect = getAudioEffect(env, thiz);
504  if (lpAudioEffect == 0) {
505        jniThrowException(env, "java/lang/IllegalStateException",
506            "Unable to retrieve AudioEffect pointer for getEnabled()");
507        return JNI_FALSE;
508    }
509
510    if (lpAudioEffect->getEnabled()) {
511        return JNI_TRUE;
512    } else {
513        return JNI_FALSE;
514    }
515}
516
517
518static jboolean
519android_media_AudioEffect_native_hasControl(JNIEnv *env, jobject thiz)
520{
521  sp<AudioEffect> lpAudioEffect = getAudioEffect(env, thiz);
522  if (lpAudioEffect == 0) {
523        jniThrowException(env, "java/lang/IllegalStateException",
524            "Unable to retrieve AudioEffect pointer for hasControl()");
525        return JNI_FALSE;
526    }
527
528    if (lpAudioEffect->initCheck() == NO_ERROR) {
529        return JNI_TRUE;
530    } else {
531        return JNI_FALSE;
532    }
533}
534
535static jint android_media_AudioEffect_native_setParameter(JNIEnv *env,
536        jobject thiz, jint psize, jbyteArray pJavaParam, jint vsize,
537        jbyteArray pJavaValue) {
538    // retrieve the AudioEffect object
539    jbyte* lpValue = NULL;
540    jbyte* lpParam = NULL;
541    jint lStatus = AUDIOEFFECT_ERROR_BAD_VALUE;
542    effect_param_t *p;
543    int voffset;
544
545    sp<AudioEffect> lpAudioEffect = getAudioEffect(env, thiz);
546    if (lpAudioEffect == 0) {
547        jniThrowException(env, "java/lang/IllegalStateException",
548                "Unable to retrieve AudioEffect pointer for setParameter()");
549        return AUDIOEFFECT_ERROR_NO_INIT;
550    }
551
552    if (psize == 0 || vsize == 0 || pJavaParam == NULL || pJavaValue == NULL) {
553        return AUDIOEFFECT_ERROR_BAD_VALUE;
554    }
555
556    // get the pointer for the param from the java array
557    lpParam = (jbyte *) env->GetPrimitiveArrayCritical(pJavaParam, NULL);
558    if (lpParam == NULL) {
559        ALOGE("setParameter: Error retrieving param pointer");
560        goto setParameter_Exit;
561    }
562
563    // get the pointer for the value from the java array
564    lpValue = (jbyte *) env->GetPrimitiveArrayCritical(pJavaValue, NULL);
565    if (lpValue == NULL) {
566        ALOGE("setParameter: Error retrieving value pointer");
567        goto setParameter_Exit;
568    }
569
570    voffset = ((psize - 1) / sizeof(int) + 1) * sizeof(int);
571    p = (effect_param_t *) malloc(sizeof(effect_param_t) + voffset + vsize);
572    memcpy(p->data, lpParam, psize);
573    p->psize = psize;
574    memcpy(p->data + voffset, lpValue, vsize);
575    p->vsize = vsize;
576
577    lStatus = lpAudioEffect->setParameter(p);
578    if (lStatus == NO_ERROR) {
579        lStatus = p->status;
580    }
581
582    free(p);
583
584setParameter_Exit:
585
586    if (lpParam != NULL) {
587        env->ReleasePrimitiveArrayCritical(pJavaParam, lpParam, 0);
588    }
589    if (lpValue != NULL) {
590        env->ReleasePrimitiveArrayCritical(pJavaValue, lpValue, 0);
591    }
592    return (jint) translateError(lStatus);
593}
594
595static jint
596android_media_AudioEffect_native_getParameter(JNIEnv *env,
597        jobject thiz, jint psize, jbyteArray pJavaParam,
598        jint vsize, jbyteArray pJavaValue) {
599    // retrieve the AudioEffect object
600    jbyte* lpParam = NULL;
601    jbyte* lpValue = NULL;
602    jint lStatus = AUDIOEFFECT_ERROR_BAD_VALUE;
603    effect_param_t *p;
604    int voffset;
605
606    sp<AudioEffect> lpAudioEffect = getAudioEffect(env, thiz);
607    if (lpAudioEffect == 0) {
608        jniThrowException(env, "java/lang/IllegalStateException",
609                "Unable to retrieve AudioEffect pointer for getParameter()");
610        return AUDIOEFFECT_ERROR_NO_INIT;
611    }
612
613    if (psize == 0 || vsize == 0 || pJavaParam == NULL || pJavaValue == NULL) {
614        return AUDIOEFFECT_ERROR_BAD_VALUE;
615    }
616
617    // get the pointer for the param from the java array
618    lpParam = (jbyte *) env->GetPrimitiveArrayCritical(pJavaParam, NULL);
619    if (lpParam == NULL) {
620        ALOGE("getParameter: Error retrieving param pointer");
621        goto getParameter_Exit;
622    }
623
624    // get the pointer for the value from the java array
625    lpValue = (jbyte *) env->GetPrimitiveArrayCritical(pJavaValue, NULL);
626    if (lpValue == NULL) {
627        ALOGE("getParameter: Error retrieving value pointer");
628        goto getParameter_Exit;
629    }
630
631    voffset = ((psize - 1) / sizeof(int) + 1) * sizeof(int);
632    p = (effect_param_t *) malloc(sizeof(effect_param_t) + voffset + vsize);
633    memcpy(p->data, lpParam, psize);
634    p->psize = psize;
635    p->vsize = vsize;
636
637    lStatus = lpAudioEffect->getParameter(p);
638    if (lStatus == NO_ERROR) {
639        lStatus = p->status;
640        if (lStatus == NO_ERROR) {
641            memcpy(lpValue, p->data + voffset, p->vsize);
642            vsize = p->vsize;
643        }
644    }
645
646    free(p);
647
648getParameter_Exit:
649
650    if (lpParam != NULL) {
651        env->ReleasePrimitiveArrayCritical(pJavaParam, lpParam, 0);
652    }
653    if (lpValue != NULL) {
654        env->ReleasePrimitiveArrayCritical(pJavaValue, lpValue, 0);
655    }
656
657    if (lStatus == NO_ERROR) {
658        return vsize;
659    }
660    return (jint) translateError(lStatus);
661}
662
663static jint android_media_AudioEffect_native_command(JNIEnv *env, jobject thiz,
664        jint cmdCode, jint cmdSize, jbyteArray jCmdData, jint replySize,
665        jbyteArray jReplyData) {
666    jbyte* pCmdData = NULL;
667    jbyte* pReplyData = NULL;
668    jint lStatus = AUDIOEFFECT_ERROR_BAD_VALUE;
669
670    sp<AudioEffect> lpAudioEffect = getAudioEffect(env, thiz);
671    if (lpAudioEffect == 0) {
672        jniThrowException(env, "java/lang/IllegalStateException",
673                "Unable to retrieve AudioEffect pointer for setParameter()");
674        return AUDIOEFFECT_ERROR_NO_INIT;
675    }
676
677    if ((cmdSize != 0 && jCmdData == NULL) || (replySize != 0 && jReplyData == NULL)) {
678        return AUDIOEFFECT_ERROR_BAD_VALUE;
679    }
680
681    // get the pointer for the command from the java array
682    if (cmdSize != 0) {
683        pCmdData = (jbyte *) env->GetPrimitiveArrayCritical(jCmdData, NULL);
684        if (pCmdData == NULL) {
685            ALOGE("setParameter: Error retrieving command pointer");
686            goto command_Exit;
687        }
688    }
689
690    // get the pointer for the reply from the java array
691    if (replySize != 0 && jReplyData != NULL) {
692        pReplyData = (jbyte *) env->GetPrimitiveArrayCritical(jReplyData, NULL);
693        if (pReplyData == NULL) {
694            ALOGE("setParameter: Error retrieving reply pointer");
695            goto command_Exit;
696        }
697    }
698
699    lStatus = translateError(lpAudioEffect->command((uint32_t)cmdCode,
700                                                    (uint32_t)cmdSize,
701                                                    pCmdData,
702                                                    (uint32_t *)&replySize,
703                                                    pReplyData));
704
705command_Exit:
706
707    if (pCmdData != NULL) {
708        env->ReleasePrimitiveArrayCritical(jCmdData, pCmdData, 0);
709    }
710    if (pReplyData != NULL) {
711        env->ReleasePrimitiveArrayCritical(jReplyData, pReplyData, 0);
712    }
713
714    if (lStatus == NO_ERROR) {
715        return replySize;
716    }
717    return lStatus;
718}
719
720static jobjectArray
721android_media_AudioEffect_native_queryEffects(JNIEnv *env, jclass clazz __unused)
722{
723    effect_descriptor_t desc;
724    char str[EFFECT_STRING_LEN_MAX];
725    uint32_t totalEffectsCount = 0;
726    uint32_t returnedEffectsCount = 0;
727    uint32_t i = 0;
728    jstring jdescType;
729    jstring jdescUuid;
730    jstring jdescConnect;
731    jstring jdescName;
732    jstring jdescImplementor;
733    jobject jdesc;
734    jobjectArray ret;
735
736    if (AudioEffect::queryNumberEffects(&totalEffectsCount) != NO_ERROR) {
737        return NULL;
738    }
739
740    jobjectArray temp = env->NewObjectArray(totalEffectsCount, fields.clazzDesc, NULL);
741    if (temp == NULL) {
742        return temp;
743    }
744
745    ALOGV("queryEffects() totalEffectsCount: %d", totalEffectsCount);
746
747    for (i = 0; i < totalEffectsCount; i++) {
748        if (AudioEffect::queryEffect(i, &desc) != NO_ERROR) {
749            goto queryEffects_failure;
750        }
751
752        if ((desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
753            jdescConnect = env->NewStringUTF("Auxiliary");
754        } else if ((desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_INSERT) {
755            jdescConnect = env->NewStringUTF("Insert");
756        } else if ((desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_PRE_PROC) {
757            jdescConnect = env->NewStringUTF("Pre Processing");
758        } else {
759            continue;
760        }
761
762        AudioEffect::guidToString(&desc.type, str, EFFECT_STRING_LEN_MAX);
763        jdescType = env->NewStringUTF(str);
764
765        AudioEffect::guidToString(&desc.uuid, str, EFFECT_STRING_LEN_MAX);
766        jdescUuid = env->NewStringUTF(str);
767
768        jdescName = env->NewStringUTF(desc.name);
769        jdescImplementor = env->NewStringUTF(desc.implementor);
770
771        jdesc = env->NewObject(fields.clazzDesc,
772                               fields.midDescCstor,
773                               jdescType,
774                               jdescUuid,
775                               jdescConnect,
776                               jdescName,
777                               jdescImplementor);
778        env->DeleteLocalRef(jdescType);
779        env->DeleteLocalRef(jdescUuid);
780        env->DeleteLocalRef(jdescConnect);
781        env->DeleteLocalRef(jdescName);
782        env->DeleteLocalRef(jdescImplementor);
783        if (jdesc == NULL) {
784            ALOGE("env->NewObject(fields.clazzDesc, fields.midDescCstor)");
785            goto queryEffects_failure;
786        }
787
788        env->SetObjectArrayElement(temp, returnedEffectsCount++, jdesc);
789   }
790
791    if (returnedEffectsCount == 0) {
792        goto queryEffects_failure;
793    }
794    ret = env->NewObjectArray(returnedEffectsCount, fields.clazzDesc, NULL);
795    if (ret == NULL) {
796        goto queryEffects_failure;
797    }
798    for (i = 0; i < returnedEffectsCount; i++) {
799        env->SetObjectArrayElement(ret, i, env->GetObjectArrayElement(temp, i));
800    }
801    env->DeleteLocalRef(temp);
802    return ret;
803
804queryEffects_failure:
805
806    if (temp != NULL) {
807        env->DeleteLocalRef(temp);
808    }
809    return NULL;
810
811}
812
813
814
815static jobjectArray
816android_media_AudioEffect_native_queryPreProcessings(JNIEnv *env, jclass clazz __unused,
817                                                     jint audioSession)
818{
819    effect_descriptor_t *descriptors = new effect_descriptor_t[AudioEffect::kMaxPreProcessing];
820    uint32_t numEffects = AudioEffect::kMaxPreProcessing;
821
822    status_t status = AudioEffect::queryDefaultPreProcessing((audio_session_t) audioSession,
823                                           descriptors,
824                                           &numEffects);
825    if (status != NO_ERROR || numEffects == 0) {
826        delete[] descriptors;
827        return NULL;
828    }
829    ALOGV("queryDefaultPreProcessing() got %d effects", numEffects);
830
831    jobjectArray ret = env->NewObjectArray(numEffects, fields.clazzDesc, NULL);
832    if (ret == NULL) {
833        delete[] descriptors;
834        return ret;
835    }
836
837    char str[EFFECT_STRING_LEN_MAX];
838    jstring jdescType;
839    jstring jdescUuid;
840    jstring jdescConnect;
841    jstring jdescName;
842    jstring jdescImplementor;
843    jobject jdesc;
844
845    for (uint32_t i = 0; i < numEffects; i++) {
846
847        AudioEffect::guidToString(&descriptors[i].type, str, EFFECT_STRING_LEN_MAX);
848        jdescType = env->NewStringUTF(str);
849        AudioEffect::guidToString(&descriptors[i].uuid, str, EFFECT_STRING_LEN_MAX);
850        jdescUuid = env->NewStringUTF(str);
851        jdescConnect = env->NewStringUTF("Pre Processing");
852        jdescName = env->NewStringUTF(descriptors[i].name);
853        jdescImplementor = env->NewStringUTF(descriptors[i].implementor);
854
855        jdesc = env->NewObject(fields.clazzDesc,
856                               fields.midDescCstor,
857                               jdescType,
858                               jdescUuid,
859                               jdescConnect,
860                               jdescName,
861                               jdescImplementor);
862        env->DeleteLocalRef(jdescType);
863        env->DeleteLocalRef(jdescUuid);
864        env->DeleteLocalRef(jdescConnect);
865        env->DeleteLocalRef(jdescName);
866        env->DeleteLocalRef(jdescImplementor);
867        if (jdesc == NULL) {
868            ALOGE("env->NewObject(fields.clazzDesc, fields.midDescCstor)");
869            env->DeleteLocalRef(ret);
870            return NULL;;
871        }
872
873        env->SetObjectArrayElement(ret, i, jdesc);
874   }
875
876   return ret;
877}
878
879// ----------------------------------------------------------------------------
880
881// Dalvik VM type signatures
882static const JNINativeMethod gMethods[] = {
883    {"native_init",          "()V",      (void *)android_media_AudioEffect_native_init},
884    {"native_setup",         "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;II[I[Ljava/lang/Object;Ljava/lang/String;)I",
885                                         (void *)android_media_AudioEffect_native_setup},
886    {"native_finalize",      "()V",      (void *)android_media_AudioEffect_native_finalize},
887    {"native_release",       "()V",      (void *)android_media_AudioEffect_native_release},
888    {"native_setEnabled",    "(Z)I",      (void *)android_media_AudioEffect_native_setEnabled},
889    {"native_getEnabled",    "()Z",      (void *)android_media_AudioEffect_native_getEnabled},
890    {"native_hasControl",    "()Z",      (void *)android_media_AudioEffect_native_hasControl},
891    {"native_setParameter",  "(I[BI[B)I",  (void *)android_media_AudioEffect_native_setParameter},
892    {"native_getParameter",  "(I[BI[B)I",  (void *)android_media_AudioEffect_native_getParameter},
893    {"native_command",       "(II[BI[B)I", (void *)android_media_AudioEffect_native_command},
894    {"native_query_effects", "()[Ljava/lang/Object;", (void *)android_media_AudioEffect_native_queryEffects},
895    {"native_query_pre_processing", "(I)[Ljava/lang/Object;",
896            (void *)android_media_AudioEffect_native_queryPreProcessings},
897};
898
899
900// ----------------------------------------------------------------------------
901
902extern int register_android_media_visualizer(JNIEnv *env);
903
904int register_android_media_AudioEffect(JNIEnv *env)
905{
906    return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods));
907}
908
909jint JNI_OnLoad(JavaVM* vm, void* reserved __unused)
910{
911
912    JNIEnv* env = NULL;
913    jint result = -1;
914
915    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
916        ALOGE("ERROR: GetEnv failed\n");
917        goto bail;
918    }
919    assert(env != NULL);
920
921    if (register_android_media_AudioEffect(env) < 0) {
922        ALOGE("ERROR: AudioEffect native registration failed\n");
923        goto bail;
924    }
925
926    if (register_android_media_visualizer(env) < 0) {
927        ALOGE("ERROR: Visualizer native registration failed\n");
928        goto bail;
929    }
930
931    /* success -- return valid version number */
932    result = JNI_VERSION_1_4;
933
934bail:
935    return result;
936}
937
938