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