There is a problem in AudioEffect and Visualizer native_setup() methods that causes a crash in the application after the mediaserver process has crashed and restarted. The problem is that the native AudioEffect/Visualizer constructor is called while the JNI is in critical state after calling GetPrimitiveArrayCritical(). As the mediaserver process just restarted, the first call to AudioSystem will cause the binder IAudioflinger interface to be reteived and a callback send to AudioSystem JNI to clear the mediaserver error state. This will call env->FindClass() and crash due to the JNI being in critical state. Also fixed a similar problem in AudioTrack JNI Change-Id: I4a9026a3e26c7f78d9b4b4bec1aac90fbee2ab62
850 lines
27 KiB
C++
850 lines
27 KiB
C++
/*
|
|
* Copyright (C) 2010 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
|
|
//#define LOG_NDEBUG 0
|
|
#define LOG_TAG "AudioEffects-JNI"
|
|
|
|
#include <utils/Log.h>
|
|
#include <nativehelper/jni.h>
|
|
#include <nativehelper/JNIHelp.h>
|
|
#include <android_runtime/AndroidRuntime.h>
|
|
#include "media/AudioEffect.h"
|
|
|
|
using namespace android;
|
|
|
|
#define AUDIOEFFECT_SUCCESS 0
|
|
#define AUDIOEFFECT_ERROR -1
|
|
#define AUDIOEFFECT_ERROR_ALREADY_EXISTS -2
|
|
#define AUDIOEFFECT_ERROR_NO_INIT -3
|
|
#define AUDIOEFFECT_ERROR_BAD_VALUE -4
|
|
#define AUDIOEFFECT_ERROR_INVALID_OPERATION -5
|
|
#define AUDIOEFFECT_ERROR_NO_MEMORY -6
|
|
#define AUDIOEFFECT_ERROR_DEAD_OBJECT -7
|
|
|
|
// ----------------------------------------------------------------------------
|
|
static const char* const kClassPathName = "android/media/audiofx/AudioEffect";
|
|
|
|
struct fields_t {
|
|
// these fields provide access from C++ to the...
|
|
jclass clazzEffect; // AudioEffect class
|
|
jmethodID midPostNativeEvent; // event post callback method
|
|
jfieldID fidNativeAudioEffect; // stores in Java the native AudioEffect object
|
|
jfieldID fidJniData; // stores in Java additional resources used by the native AudioEffect
|
|
jclass clazzDesc; // AudioEffect.Descriptor class
|
|
jmethodID midDescCstor; // AudioEffect.Descriptor class constructor
|
|
};
|
|
static fields_t fields;
|
|
|
|
struct effect_callback_cookie {
|
|
jclass audioEffect_class; // AudioEffect class
|
|
jobject audioEffect_ref; // AudioEffect object instance
|
|
};
|
|
|
|
// ----------------------------------------------------------------------------
|
|
class AudioEffectJniStorage {
|
|
public:
|
|
effect_callback_cookie mCallbackData;
|
|
|
|
AudioEffectJniStorage() {
|
|
}
|
|
|
|
~AudioEffectJniStorage() {
|
|
}
|
|
|
|
};
|
|
|
|
|
|
static jint translateError(int code) {
|
|
switch(code) {
|
|
case NO_ERROR:
|
|
return AUDIOEFFECT_SUCCESS;
|
|
case ALREADY_EXISTS:
|
|
return AUDIOEFFECT_ERROR_ALREADY_EXISTS;
|
|
case NO_INIT:
|
|
return AUDIOEFFECT_ERROR_NO_INIT;
|
|
case BAD_VALUE:
|
|
return AUDIOEFFECT_ERROR_BAD_VALUE;
|
|
case INVALID_OPERATION:
|
|
return AUDIOEFFECT_ERROR_INVALID_OPERATION;
|
|
case NO_MEMORY:
|
|
return AUDIOEFFECT_ERROR_NO_MEMORY;
|
|
case DEAD_OBJECT:
|
|
return AUDIOEFFECT_ERROR_DEAD_OBJECT;
|
|
default:
|
|
return AUDIOEFFECT_ERROR;
|
|
}
|
|
}
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
static void effectCallback(int event, void* user, void *info) {
|
|
|
|
effect_param_t *p;
|
|
int arg1 = 0;
|
|
int arg2 = 0;
|
|
jobject obj = NULL;
|
|
jbyteArray array = NULL;
|
|
jbyte *bytes;
|
|
bool param;
|
|
size_t size;
|
|
|
|
effect_callback_cookie *callbackInfo = (effect_callback_cookie *)user;
|
|
JNIEnv *env = AndroidRuntime::getJNIEnv();
|
|
|
|
LOGV("effectCallback: callbackInfo %p, audioEffect_ref %p audioEffect_class %p",
|
|
callbackInfo,
|
|
callbackInfo->audioEffect_ref,
|
|
callbackInfo->audioEffect_class);
|
|
|
|
if (!user || !env) {
|
|
LOGW("effectCallback error user %p, env %p", user, env);
|
|
return;
|
|
}
|
|
|
|
switch (event) {
|
|
case AudioEffect::EVENT_CONTROL_STATUS_CHANGED:
|
|
if (info == 0) {
|
|
LOGW("EVENT_CONTROL_STATUS_CHANGED info == NULL");
|
|
goto effectCallback_Exit;
|
|
}
|
|
param = *(bool *)info;
|
|
arg1 = (int)param;
|
|
LOGV("EVENT_CONTROL_STATUS_CHANGED");
|
|
break;
|
|
case AudioEffect::EVENT_ENABLE_STATUS_CHANGED:
|
|
if (info == 0) {
|
|
LOGW("EVENT_ENABLE_STATUS_CHANGED info == NULL");
|
|
goto effectCallback_Exit;
|
|
}
|
|
param = *(bool *)info;
|
|
arg1 = (int)param;
|
|
LOGV("EVENT_ENABLE_STATUS_CHANGED");
|
|
break;
|
|
case AudioEffect::EVENT_PARAMETER_CHANGED:
|
|
if (info == 0) {
|
|
LOGW("EVENT_PARAMETER_CHANGED info == NULL");
|
|
goto effectCallback_Exit;
|
|
}
|
|
p = (effect_param_t *)info;
|
|
if (p->psize == 0 || p->vsize == 0) {
|
|
goto effectCallback_Exit;
|
|
}
|
|
// arg1 contains offset of parameter value from start of byte array
|
|
arg1 = sizeof(effect_param_t) + ((p->psize - 1) / sizeof(int) + 1) * sizeof(int);
|
|
size = arg1 + p->vsize;
|
|
array = env->NewByteArray(size);
|
|
if (array == NULL) {
|
|
LOGE("effectCallback: Couldn't allocate byte array for parameter data");
|
|
goto effectCallback_Exit;
|
|
}
|
|
bytes = env->GetByteArrayElements(array, NULL);
|
|
memcpy(bytes, p, size);
|
|
env->ReleaseByteArrayElements(array, bytes, 0);
|
|
obj = array;
|
|
LOGV("EVENT_PARAMETER_CHANGED");
|
|
break;
|
|
case AudioEffect::EVENT_ERROR:
|
|
LOGW("EVENT_ERROR");
|
|
break;
|
|
}
|
|
|
|
env->CallStaticVoidMethod(
|
|
callbackInfo->audioEffect_class,
|
|
fields.midPostNativeEvent,
|
|
callbackInfo->audioEffect_ref, event, arg1, arg2, obj);
|
|
|
|
effectCallback_Exit:
|
|
if (array) {
|
|
env->DeleteLocalRef(array);
|
|
}
|
|
|
|
if (env->ExceptionCheck()) {
|
|
env->ExceptionDescribe();
|
|
env->ExceptionClear();
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// This function gets some field IDs, which in turn causes class initialization.
|
|
// It is called from a static block in AudioEffect, which won't run until the
|
|
// first time an instance of this class is used.
|
|
static void
|
|
android_media_AudioEffect_native_init(JNIEnv *env)
|
|
{
|
|
|
|
LOGV("android_media_AudioEffect_native_init");
|
|
|
|
fields.clazzEffect = NULL;
|
|
fields.clazzDesc = NULL;
|
|
|
|
// Get the AudioEffect class
|
|
jclass clazz = env->FindClass(kClassPathName);
|
|
if (clazz == NULL) {
|
|
LOGE("Can't find %s", kClassPathName);
|
|
return;
|
|
}
|
|
|
|
fields.clazzEffect = (jclass)env->NewGlobalRef(clazz);
|
|
|
|
// Get the postEvent method
|
|
fields.midPostNativeEvent = env->GetStaticMethodID(
|
|
fields.clazzEffect,
|
|
"postEventFromNative", "(Ljava/lang/Object;IIILjava/lang/Object;)V");
|
|
if (fields.midPostNativeEvent == NULL) {
|
|
LOGE("Can't find AudioEffect.%s", "postEventFromNative");
|
|
return;
|
|
}
|
|
|
|
// Get the variables fields
|
|
// nativeTrackInJavaObj
|
|
fields.fidNativeAudioEffect = env->GetFieldID(
|
|
fields.clazzEffect,
|
|
"mNativeAudioEffect", "I");
|
|
if (fields.fidNativeAudioEffect == NULL) {
|
|
LOGE("Can't find AudioEffect.%s", "mNativeAudioEffect");
|
|
return;
|
|
}
|
|
// fidJniData;
|
|
fields.fidJniData = env->GetFieldID(
|
|
fields.clazzEffect,
|
|
"mJniData", "I");
|
|
if (fields.fidJniData == NULL) {
|
|
LOGE("Can't find AudioEffect.%s", "mJniData");
|
|
return;
|
|
}
|
|
|
|
clazz = env->FindClass("android/media/audiofx/AudioEffect$Descriptor");
|
|
if (clazz == NULL) {
|
|
LOGE("Can't find android/media/audiofx/AudioEffect$Descriptor class");
|
|
return;
|
|
}
|
|
fields.clazzDesc = (jclass)env->NewGlobalRef(clazz);
|
|
|
|
fields.midDescCstor
|
|
= env->GetMethodID(
|
|
fields.clazzDesc,
|
|
"<init>",
|
|
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
|
|
if (fields.midDescCstor == NULL) {
|
|
LOGE("Can't find android/media/audiofx/AudioEffect$Descriptor class constructor");
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
static jint
|
|
android_media_AudioEffect_native_setup(JNIEnv *env, jobject thiz, jobject weak_this,
|
|
jstring type, jstring uuid, jint priority, jint sessionId, jintArray jId, jobjectArray javadesc)
|
|
{
|
|
LOGV("android_media_AudioEffect_native_setup");
|
|
AudioEffectJniStorage* lpJniStorage = NULL;
|
|
int lStatus = AUDIOEFFECT_ERROR_NO_MEMORY;
|
|
AudioEffect* lpAudioEffect = NULL;
|
|
jint* nId = NULL;
|
|
const char *typeStr = NULL;
|
|
const char *uuidStr = NULL;
|
|
effect_descriptor_t desc;
|
|
jobject jdesc;
|
|
char str[EFFECT_STRING_LEN_MAX];
|
|
jstring jdescType;
|
|
jstring jdescUuid;
|
|
jstring jdescConnect;
|
|
jstring jdescName;
|
|
jstring jdescImplementor;
|
|
|
|
if (type != NULL) {
|
|
typeStr = env->GetStringUTFChars(type, NULL);
|
|
if (typeStr == NULL) { // Out of memory
|
|
jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
|
|
goto setup_failure;
|
|
}
|
|
}
|
|
|
|
if (uuid != NULL) {
|
|
uuidStr = env->GetStringUTFChars(uuid, NULL);
|
|
if (uuidStr == NULL) { // Out of memory
|
|
jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
|
|
goto setup_failure;
|
|
}
|
|
}
|
|
|
|
if (typeStr == NULL && uuidStr == NULL) {
|
|
lStatus = AUDIOEFFECT_ERROR_BAD_VALUE;
|
|
goto setup_failure;
|
|
}
|
|
|
|
lpJniStorage = new AudioEffectJniStorage();
|
|
if (lpJniStorage == NULL) {
|
|
LOGE("setup: Error creating JNI Storage");
|
|
goto setup_failure;
|
|
}
|
|
|
|
lpJniStorage->mCallbackData.audioEffect_class = (jclass)env->NewGlobalRef(fields.clazzEffect);
|
|
// we use a weak reference so the AudioEffect object can be garbage collected.
|
|
lpJniStorage->mCallbackData.audioEffect_ref = env->NewGlobalRef(weak_this);
|
|
|
|
LOGV("setup: lpJniStorage: %p audioEffect_ref %p audioEffect_class %p, &mCallbackData %p",
|
|
lpJniStorage,
|
|
lpJniStorage->mCallbackData.audioEffect_ref,
|
|
lpJniStorage->mCallbackData.audioEffect_class,
|
|
&lpJniStorage->mCallbackData);
|
|
|
|
if (jId == NULL) {
|
|
LOGE("setup: NULL java array for id pointer");
|
|
lStatus = AUDIOEFFECT_ERROR_BAD_VALUE;
|
|
goto setup_failure;
|
|
}
|
|
|
|
// create the native AudioEffect object
|
|
lpAudioEffect = new AudioEffect(typeStr,
|
|
uuidStr,
|
|
priority,
|
|
effectCallback,
|
|
&lpJniStorage->mCallbackData,
|
|
sessionId,
|
|
0);
|
|
if (lpAudioEffect == NULL) {
|
|
LOGE("Error creating AudioEffect");
|
|
goto setup_failure;
|
|
}
|
|
|
|
lStatus = translateError(lpAudioEffect->initCheck());
|
|
if (lStatus != AUDIOEFFECT_SUCCESS && lStatus != AUDIOEFFECT_ERROR_ALREADY_EXISTS) {
|
|
LOGE("AudioEffect initCheck failed %d", lStatus);
|
|
goto setup_failure;
|
|
}
|
|
|
|
nId = (jint *) env->GetPrimitiveArrayCritical(jId, NULL);
|
|
if (nId == NULL) {
|
|
LOGE("setup: Error retrieving id pointer");
|
|
lStatus = AUDIOEFFECT_ERROR_BAD_VALUE;
|
|
goto setup_failure;
|
|
}
|
|
nId[0] = lpAudioEffect->id();
|
|
env->ReleasePrimitiveArrayCritical(jId, nId, 0);
|
|
nId = NULL;
|
|
|
|
if (typeStr) {
|
|
env->ReleaseStringUTFChars(type, typeStr);
|
|
typeStr = NULL;
|
|
}
|
|
|
|
if (uuidStr) {
|
|
env->ReleaseStringUTFChars(uuid, uuidStr);
|
|
uuidStr = NULL;
|
|
}
|
|
|
|
// get the effect descriptor
|
|
desc = lpAudioEffect->descriptor();
|
|
|
|
AudioEffect::guidToString(&desc.type, str, EFFECT_STRING_LEN_MAX);
|
|
jdescType = env->NewStringUTF(str);
|
|
|
|
AudioEffect::guidToString(&desc.uuid, str, EFFECT_STRING_LEN_MAX);
|
|
jdescUuid = env->NewStringUTF(str);
|
|
|
|
if ((desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
|
|
jdescConnect = env->NewStringUTF("Auxiliary");
|
|
} else {
|
|
jdescConnect = env->NewStringUTF("Insert");
|
|
}
|
|
|
|
jdescName = env->NewStringUTF(desc.name);
|
|
jdescImplementor = env->NewStringUTF(desc.implementor);
|
|
|
|
jdesc = env->NewObject(fields.clazzDesc,
|
|
fields.midDescCstor,
|
|
jdescType,
|
|
jdescUuid,
|
|
jdescConnect,
|
|
jdescName,
|
|
jdescImplementor);
|
|
env->DeleteLocalRef(jdescType);
|
|
env->DeleteLocalRef(jdescUuid);
|
|
env->DeleteLocalRef(jdescConnect);
|
|
env->DeleteLocalRef(jdescName);
|
|
env->DeleteLocalRef(jdescImplementor);
|
|
if (jdesc == NULL) {
|
|
LOGE("env->NewObject(fields.clazzDesc, fields.midDescCstor)");
|
|
goto setup_failure;
|
|
}
|
|
|
|
env->SetObjectArrayElement(javadesc, 0, jdesc);
|
|
|
|
env->SetIntField(thiz, fields.fidNativeAudioEffect, (int)lpAudioEffect);
|
|
|
|
env->SetIntField(thiz, fields.fidJniData, (int)lpJniStorage);
|
|
|
|
return AUDIOEFFECT_SUCCESS;
|
|
|
|
// failures:
|
|
setup_failure:
|
|
|
|
if (nId != NULL) {
|
|
env->ReleasePrimitiveArrayCritical(jId, nId, 0);
|
|
}
|
|
|
|
if (lpAudioEffect) {
|
|
delete lpAudioEffect;
|
|
}
|
|
env->SetIntField(thiz, fields.fidNativeAudioEffect, 0);
|
|
|
|
if (lpJniStorage) {
|
|
delete lpJniStorage;
|
|
}
|
|
env->SetIntField(thiz, fields.fidJniData, 0);
|
|
|
|
if (uuidStr != NULL) {
|
|
env->ReleaseStringUTFChars(uuid, uuidStr);
|
|
}
|
|
|
|
if (typeStr != NULL) {
|
|
env->ReleaseStringUTFChars(type, typeStr);
|
|
}
|
|
|
|
return lStatus;
|
|
}
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
static void android_media_AudioEffect_native_finalize(JNIEnv *env, jobject thiz) {
|
|
LOGV("android_media_AudioEffect_native_finalize jobject: %x\n", (int)thiz);
|
|
|
|
// delete the AudioEffect object
|
|
AudioEffect* lpAudioEffect = (AudioEffect *)env->GetIntField(
|
|
thiz, fields.fidNativeAudioEffect);
|
|
if (lpAudioEffect) {
|
|
LOGV("deleting AudioEffect: %x\n", (int)lpAudioEffect);
|
|
delete lpAudioEffect;
|
|
}
|
|
|
|
// delete the JNI data
|
|
AudioEffectJniStorage* lpJniStorage = (AudioEffectJniStorage *)env->GetIntField(
|
|
thiz, fields.fidJniData);
|
|
if (lpJniStorage) {
|
|
LOGV("deleting pJniStorage: %x\n", (int)lpJniStorage);
|
|
delete lpJniStorage;
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
static void android_media_AudioEffect_native_release(JNIEnv *env, jobject thiz) {
|
|
|
|
// do everything a call to finalize would
|
|
android_media_AudioEffect_native_finalize(env, thiz);
|
|
// + reset the native resources in the Java object so any attempt to access
|
|
// them after a call to release fails.
|
|
env->SetIntField(thiz, fields.fidNativeAudioEffect, 0);
|
|
env->SetIntField(thiz, fields.fidJniData, 0);
|
|
}
|
|
|
|
static jint
|
|
android_media_AudioEffect_native_setEnabled(JNIEnv *env, jobject thiz, jboolean enabled)
|
|
{
|
|
// retrieve the AudioEffect object
|
|
AudioEffect* lpAudioEffect = (AudioEffect *)env->GetIntField(
|
|
thiz, fields.fidNativeAudioEffect);
|
|
|
|
if (lpAudioEffect == NULL) {
|
|
jniThrowException(env, "java/lang/IllegalStateException",
|
|
"Unable to retrieve AudioEffect pointer for enable()");
|
|
return AUDIOEFFECT_ERROR_NO_INIT;
|
|
}
|
|
|
|
return translateError(lpAudioEffect->setEnabled(enabled));
|
|
}
|
|
|
|
static jboolean
|
|
android_media_AudioEffect_native_getEnabled(JNIEnv *env, jobject thiz)
|
|
{
|
|
// retrieve the AudioEffect object
|
|
AudioEffect* lpAudioEffect = (AudioEffect *)env->GetIntField(
|
|
thiz, fields.fidNativeAudioEffect);
|
|
|
|
if (lpAudioEffect == NULL) {
|
|
jniThrowException(env, "java/lang/IllegalStateException",
|
|
"Unable to retrieve AudioEffect pointer for getEnabled()");
|
|
return false;
|
|
}
|
|
|
|
return (jboolean)lpAudioEffect->getEnabled();
|
|
}
|
|
|
|
|
|
static jboolean
|
|
android_media_AudioEffect_native_hasControl(JNIEnv *env, jobject thiz)
|
|
{
|
|
// retrieve the AudioEffect object
|
|
AudioEffect* lpAudioEffect = (AudioEffect *)env->GetIntField(
|
|
thiz, fields.fidNativeAudioEffect);
|
|
|
|
if (lpAudioEffect == NULL) {
|
|
jniThrowException(env, "java/lang/IllegalStateException",
|
|
"Unable to retrieve AudioEffect pointer for hasControl()");
|
|
return false;
|
|
}
|
|
|
|
if (lpAudioEffect->initCheck() == NO_ERROR) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static jint android_media_AudioEffect_native_setParameter(JNIEnv *env,
|
|
jobject thiz, int psize, jbyteArray pJavaParam, int vsize,
|
|
jbyteArray pJavaValue) {
|
|
// retrieve the AudioEffect object
|
|
jbyte* lpValue = NULL;
|
|
jbyte* lpParam = NULL;
|
|
jint lStatus = AUDIOEFFECT_ERROR_BAD_VALUE;
|
|
effect_param_t *p;
|
|
int voffset;
|
|
|
|
AudioEffect* lpAudioEffect = (AudioEffect *) env->GetIntField(thiz,
|
|
fields.fidNativeAudioEffect);
|
|
|
|
if (lpAudioEffect == NULL) {
|
|
jniThrowException(env, "java/lang/IllegalStateException",
|
|
"Unable to retrieve AudioEffect pointer for setParameter()");
|
|
return AUDIOEFFECT_ERROR_NO_INIT;
|
|
}
|
|
|
|
if (psize == 0 || vsize == 0 || pJavaParam == NULL || pJavaValue == NULL) {
|
|
return AUDIOEFFECT_ERROR_BAD_VALUE;
|
|
}
|
|
|
|
// get the pointer for the param from the java array
|
|
lpParam = (jbyte *) env->GetPrimitiveArrayCritical(pJavaParam, NULL);
|
|
if (lpParam == NULL) {
|
|
LOGE("setParameter: Error retrieving param pointer");
|
|
goto setParameter_Exit;
|
|
}
|
|
|
|
// get the pointer for the value from the java array
|
|
lpValue = (jbyte *) env->GetPrimitiveArrayCritical(pJavaValue, NULL);
|
|
if (lpValue == NULL) {
|
|
LOGE("setParameter: Error retrieving value pointer");
|
|
goto setParameter_Exit;
|
|
}
|
|
|
|
voffset = ((psize - 1) / sizeof(int) + 1) * sizeof(int);
|
|
p = (effect_param_t *) malloc(sizeof(effect_param_t) + voffset + vsize);
|
|
memcpy(p->data, lpParam, psize);
|
|
p->psize = psize;
|
|
memcpy(p->data + voffset, lpValue, vsize);
|
|
p->vsize = vsize;
|
|
|
|
lStatus = lpAudioEffect->setParameter(p);
|
|
if (lStatus == NO_ERROR) {
|
|
lStatus = p->status;
|
|
}
|
|
|
|
free(p);
|
|
|
|
setParameter_Exit:
|
|
|
|
if (lpParam != NULL) {
|
|
env->ReleasePrimitiveArrayCritical(pJavaParam, lpParam, 0);
|
|
}
|
|
if (lpValue != NULL) {
|
|
env->ReleasePrimitiveArrayCritical(pJavaValue, lpValue, 0);
|
|
}
|
|
return translateError(lStatus);
|
|
}
|
|
|
|
static jint
|
|
android_media_AudioEffect_native_getParameter(JNIEnv *env,
|
|
jobject thiz, int psize, jbyteArray pJavaParam,
|
|
jintArray pJavaValueSize, jbyteArray pJavaValue) {
|
|
// retrieve the AudioEffect object
|
|
jbyte* lpParam = NULL;
|
|
jbyte* lpValue = NULL;
|
|
jbyte* lpValueSize = NULL;
|
|
jint lStatus = AUDIOEFFECT_ERROR_BAD_VALUE;
|
|
effect_param_t *p;
|
|
int voffset;
|
|
|
|
AudioEffect* lpAudioEffect = (AudioEffect *) env->GetIntField(thiz,
|
|
fields.fidNativeAudioEffect);
|
|
|
|
if (lpAudioEffect == NULL) {
|
|
jniThrowException(env, "java/lang/IllegalStateException",
|
|
"Unable to retrieve AudioEffect pointer for getParameter()");
|
|
return AUDIOEFFECT_ERROR_NO_INIT;
|
|
}
|
|
|
|
if (psize == 0 || pJavaValueSize == NULL || pJavaParam == NULL || pJavaValue == NULL) {
|
|
return AUDIOEFFECT_ERROR_BAD_VALUE;
|
|
}
|
|
|
|
// get the pointer for the param from the java array
|
|
lpParam = (jbyte *) env->GetPrimitiveArrayCritical(pJavaParam, NULL);
|
|
if (lpParam == NULL) {
|
|
LOGE("getParameter: Error retrieving param pointer");
|
|
goto getParameter_Exit;
|
|
}
|
|
|
|
// get the pointer for the value from the java array
|
|
lpValue = (jbyte *) env->GetPrimitiveArrayCritical(pJavaValue, NULL);
|
|
if (lpValue == NULL) {
|
|
LOGE("getParameter: Error retrieving value pointer");
|
|
goto getParameter_Exit;
|
|
}
|
|
|
|
// get the pointer for the value size from the java array
|
|
lpValueSize = (jbyte *) env->GetPrimitiveArrayCritical(pJavaValueSize, NULL);
|
|
if (lpValueSize == NULL) {
|
|
LOGE("getParameter: Error retrieving value size pointer");
|
|
goto getParameter_Exit;
|
|
}
|
|
|
|
voffset = ((psize - 1) / sizeof(int) + 1) * sizeof(int);
|
|
p = (effect_param_t *) malloc(sizeof(effect_param_t) + voffset
|
|
+ lpValueSize[0]);
|
|
memcpy(p->data, lpParam, psize);
|
|
p->psize = psize;
|
|
p->vsize = lpValueSize[0];
|
|
|
|
lStatus = lpAudioEffect->getParameter(p);
|
|
if (lStatus == NO_ERROR) {
|
|
lStatus = p->status;
|
|
if (lStatus == NO_ERROR) {
|
|
memcpy(lpValue, p->data + voffset, p->vsize);
|
|
lpValueSize[0] = p->vsize;
|
|
}
|
|
}
|
|
|
|
free(p);
|
|
|
|
getParameter_Exit:
|
|
|
|
if (lpParam != NULL) {
|
|
env->ReleasePrimitiveArrayCritical(pJavaParam, lpParam, 0);
|
|
}
|
|
if (lpValue != NULL) {
|
|
env->ReleasePrimitiveArrayCritical(pJavaValue, lpValue, 0);
|
|
}
|
|
if (lpValueSize != NULL) {
|
|
env->ReleasePrimitiveArrayCritical(pJavaValueSize, lpValueSize, 0);
|
|
}
|
|
|
|
return translateError(lStatus);
|
|
}
|
|
|
|
static jint android_media_AudioEffect_native_command(JNIEnv *env, jobject thiz,
|
|
jint cmdCode, jint cmdSize, jbyteArray jCmdData, jintArray jReplySize,
|
|
jbyteArray jReplyData) {
|
|
jbyte* pCmdData = NULL;
|
|
jbyte* pReplyData = NULL;
|
|
jint* pReplySize = NULL;
|
|
jint lStatus = AUDIOEFFECT_ERROR_BAD_VALUE;
|
|
|
|
// retrieve the AudioEffect object
|
|
AudioEffect* lpAudioEffect = (AudioEffect *) env->GetIntField(thiz,
|
|
fields.fidNativeAudioEffect);
|
|
|
|
if (lpAudioEffect == NULL) {
|
|
jniThrowException(env, "java/lang/IllegalStateException",
|
|
"Unable to retrieve AudioEffect pointer for setParameter()");
|
|
return AUDIOEFFECT_ERROR_NO_INIT;
|
|
}
|
|
|
|
if ((cmdSize != 0 && jCmdData == NULL) || (jReplySize != NULL && jReplyData == NULL)) {
|
|
return AUDIOEFFECT_ERROR_BAD_VALUE;
|
|
}
|
|
|
|
// get the pointer for the command from the java array
|
|
if (cmdSize != 0) {
|
|
pCmdData = (jbyte *) env->GetPrimitiveArrayCritical(jCmdData, NULL);
|
|
if (pCmdData == NULL) {
|
|
LOGE("setParameter: Error retrieving command pointer");
|
|
goto command_Exit;
|
|
}
|
|
}
|
|
|
|
// get the pointer for the reply size from the java array
|
|
if (jReplySize != NULL) {
|
|
pReplySize = (jint *) env->GetPrimitiveArrayCritical(jReplySize, NULL);
|
|
if (pReplySize == NULL) {
|
|
LOGE("setParameter: Error retrieving reply pointer");
|
|
goto command_Exit;
|
|
}
|
|
}
|
|
|
|
// get the pointer for the reply from the java array
|
|
if (pReplySize != NULL && pReplySize[0] != 0 && jReplyData != NULL) {
|
|
pReplyData = (jbyte *) env->GetPrimitiveArrayCritical(jReplyData, NULL);
|
|
if (pReplyData == NULL) {
|
|
LOGE("setParameter: Error retrieving reply pointer");
|
|
goto command_Exit;
|
|
}
|
|
}
|
|
|
|
lStatus = translateError(lpAudioEffect->command((uint32_t)cmdCode,
|
|
(uint32_t)cmdSize,
|
|
pCmdData,
|
|
(uint32_t *)pReplySize,
|
|
pReplyData));
|
|
|
|
command_Exit:
|
|
|
|
if (pCmdData != NULL) {
|
|
env->ReleasePrimitiveArrayCritical(jCmdData, pCmdData, 0);
|
|
}
|
|
if (pReplyData != NULL) {
|
|
env->ReleasePrimitiveArrayCritical(jReplyData, pReplyData, 0);
|
|
}
|
|
if (pReplySize != NULL) {
|
|
env->ReleasePrimitiveArrayCritical(jReplySize, pReplySize, 0);
|
|
}
|
|
|
|
return lStatus;
|
|
}
|
|
|
|
static jobjectArray
|
|
android_media_AudioEffect_native_queryEffects(JNIEnv *env, jclass clazz)
|
|
{
|
|
effect_descriptor_t desc;
|
|
char str[EFFECT_STRING_LEN_MAX];
|
|
uint32_t numEffects;
|
|
uint32_t i = 0;
|
|
jstring jdescType;
|
|
jstring jdescUuid;
|
|
jstring jdescConnect;
|
|
jstring jdescName;
|
|
jstring jdescImplementor;
|
|
jobject jdesc;
|
|
|
|
AudioEffect::queryNumberEffects(&numEffects);
|
|
jobjectArray ret = env->NewObjectArray(numEffects, fields.clazzDesc, NULL);
|
|
if (ret == NULL) {
|
|
return ret;
|
|
}
|
|
|
|
LOGV("queryEffects() numEffects: %d", numEffects);
|
|
|
|
for (i = 0; i < numEffects; i++) {
|
|
if (AudioEffect::queryEffect(i, &desc) != NO_ERROR) {
|
|
goto queryEffects_failure;
|
|
}
|
|
|
|
AudioEffect::guidToString(&desc.type, str, EFFECT_STRING_LEN_MAX);
|
|
jdescType = env->NewStringUTF(str);
|
|
|
|
AudioEffect::guidToString(&desc.uuid, str, EFFECT_STRING_LEN_MAX);
|
|
jdescUuid = env->NewStringUTF(str);
|
|
|
|
if ((desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
|
|
jdescConnect = env->NewStringUTF("Auxiliary");
|
|
} else {
|
|
jdescConnect = env->NewStringUTF("Insert");
|
|
}
|
|
|
|
jdescName = env->NewStringUTF(desc.name);
|
|
jdescImplementor = env->NewStringUTF(desc.implementor);
|
|
|
|
jdesc = env->NewObject(fields.clazzDesc,
|
|
fields.midDescCstor,
|
|
jdescType,
|
|
jdescUuid,
|
|
jdescConnect,
|
|
jdescName,
|
|
jdescImplementor);
|
|
env->DeleteLocalRef(jdescType);
|
|
env->DeleteLocalRef(jdescUuid);
|
|
env->DeleteLocalRef(jdescConnect);
|
|
env->DeleteLocalRef(jdescName);
|
|
env->DeleteLocalRef(jdescImplementor);
|
|
if (jdesc == NULL) {
|
|
LOGE("env->NewObject(fields.clazzDesc, fields.midDescCstor)");
|
|
goto queryEffects_failure;
|
|
}
|
|
|
|
env->SetObjectArrayElement(ret, i, jdesc);
|
|
}
|
|
|
|
return ret;
|
|
|
|
queryEffects_failure:
|
|
|
|
if (ret != NULL) {
|
|
env->DeleteLocalRef(ret);
|
|
}
|
|
return NULL;
|
|
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// Dalvik VM type signatures
|
|
static JNINativeMethod gMethods[] = {
|
|
{"native_init", "()V", (void *)android_media_AudioEffect_native_init},
|
|
{"native_setup", "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;II[I[Ljava/lang/Object;)I",
|
|
(void *)android_media_AudioEffect_native_setup},
|
|
{"native_finalize", "()V", (void *)android_media_AudioEffect_native_finalize},
|
|
{"native_release", "()V", (void *)android_media_AudioEffect_native_release},
|
|
{"native_setEnabled", "(Z)I", (void *)android_media_AudioEffect_native_setEnabled},
|
|
{"native_getEnabled", "()Z", (void *)android_media_AudioEffect_native_getEnabled},
|
|
{"native_hasControl", "()Z", (void *)android_media_AudioEffect_native_hasControl},
|
|
{"native_setParameter", "(I[BI[B)I", (void *)android_media_AudioEffect_native_setParameter},
|
|
{"native_getParameter", "(I[B[I[B)I", (void *)android_media_AudioEffect_native_getParameter},
|
|
{"native_command", "(II[B[I[B)I", (void *)android_media_AudioEffect_native_command},
|
|
{"native_query_effects", "()[Ljava/lang/Object;", (void *)android_media_AudioEffect_native_queryEffects},
|
|
};
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
extern int register_android_media_visualizer(JNIEnv *env);
|
|
|
|
int register_android_media_AudioEffect(JNIEnv *env)
|
|
{
|
|
return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods));
|
|
}
|
|
|
|
jint JNI_OnLoad(JavaVM* vm, void* reserved)
|
|
{
|
|
|
|
JNIEnv* env = NULL;
|
|
jint result = -1;
|
|
|
|
if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
|
|
LOGE("ERROR: GetEnv failed\n");
|
|
goto bail;
|
|
}
|
|
assert(env != NULL);
|
|
|
|
if (register_android_media_AudioEffect(env) < 0) {
|
|
LOGE("ERROR: AudioEffect native registration failed\n");
|
|
goto bail;
|
|
}
|
|
|
|
if (register_android_media_visualizer(env) < 0) {
|
|
LOGE("ERROR: Visualizer native registration failed\n");
|
|
goto bail;
|
|
}
|
|
|
|
/* success -- return valid version number */
|
|
result = JNI_VERSION_1_4;
|
|
|
|
bail:
|
|
return result;
|
|
}
|
|
|