2fb43ef8c0
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
505 lines
16 KiB
C++
505 lines
16 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 "visualizers-JNI"
|
|
|
|
#include <utils/Log.h>
|
|
#include <nativehelper/jni.h>
|
|
#include <nativehelper/JNIHelp.h>
|
|
#include <android_runtime/AndroidRuntime.h>
|
|
#include "media/Visualizer.h"
|
|
|
|
using namespace android;
|
|
|
|
#define VISUALIZER_SUCCESS 0
|
|
#define VISUALIZER_ERROR -1
|
|
#define VISUALIZER_ERROR_ALREADY_EXISTS -2
|
|
#define VISUALIZER_ERROR_NO_INIT -3
|
|
#define VISUALIZER_ERROR_BAD_VALUE -4
|
|
#define VISUALIZER_ERROR_INVALID_OPERATION -5
|
|
#define VISUALIZER_ERROR_NO_MEMORY -6
|
|
#define VISUALIZER_ERROR_DEAD_OBJECT -7
|
|
|
|
#define NATIVE_EVENT_PCM_CAPTURE 0
|
|
#define NATIVE_EVENT_FFT_CAPTURE 1
|
|
|
|
// ----------------------------------------------------------------------------
|
|
static const char* const kClassPathName = "android/media/audiofx/Visualizer";
|
|
|
|
struct fields_t {
|
|
// these fields provide access from C++ to the...
|
|
jclass clazzEffect; // Visualizer class
|
|
jmethodID midPostNativeEvent; // event post callback method
|
|
jfieldID fidNativeVisualizer; // stores in Java the native Visualizer object
|
|
jfieldID fidJniData; // stores in Java additional resources used by the native Visualizer
|
|
};
|
|
static fields_t fields;
|
|
|
|
struct visualizer_callback_cookie {
|
|
jclass visualizer_class; // Visualizer class
|
|
jobject visualizer_ref; // Visualizer object instance
|
|
};
|
|
|
|
// ----------------------------------------------------------------------------
|
|
class visualizerJniStorage {
|
|
public:
|
|
visualizer_callback_cookie mCallbackData;
|
|
|
|
visualizerJniStorage() {
|
|
}
|
|
|
|
~visualizerJniStorage() {
|
|
}
|
|
|
|
};
|
|
|
|
|
|
static jint translateError(int code) {
|
|
switch(code) {
|
|
case NO_ERROR:
|
|
return VISUALIZER_SUCCESS;
|
|
case ALREADY_EXISTS:
|
|
return VISUALIZER_ERROR_ALREADY_EXISTS;
|
|
case NO_INIT:
|
|
return VISUALIZER_ERROR_NO_INIT;
|
|
case BAD_VALUE:
|
|
return VISUALIZER_ERROR_BAD_VALUE;
|
|
case INVALID_OPERATION:
|
|
return VISUALIZER_ERROR_INVALID_OPERATION;
|
|
case NO_MEMORY:
|
|
return VISUALIZER_ERROR_NO_MEMORY;
|
|
case DEAD_OBJECT:
|
|
return VISUALIZER_ERROR_DEAD_OBJECT;
|
|
default:
|
|
return VISUALIZER_ERROR;
|
|
}
|
|
}
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
static void captureCallback(void* user,
|
|
uint32_t waveformSize,
|
|
uint8_t *waveform,
|
|
uint32_t fftSize,
|
|
uint8_t *fft,
|
|
uint32_t samplingrate) {
|
|
|
|
int arg1 = 0;
|
|
int arg2 = 0;
|
|
size_t size;
|
|
|
|
visualizer_callback_cookie *callbackInfo = (visualizer_callback_cookie *)user;
|
|
JNIEnv *env = AndroidRuntime::getJNIEnv();
|
|
|
|
LOGV("captureCallback: callbackInfo %p, visualizer_ref %p visualizer_class %p",
|
|
callbackInfo,
|
|
callbackInfo->visualizer_ref,
|
|
callbackInfo->visualizer_class);
|
|
|
|
if (!user || !env) {
|
|
LOGW("captureCallback error user %p, env %p", user, env);
|
|
return;
|
|
}
|
|
|
|
if (waveformSize != 0 && waveform != NULL) {
|
|
jbyteArray jArray = env->NewByteArray(waveformSize);
|
|
if (jArray != NULL) {
|
|
jbyte *nArray = env->GetByteArrayElements(jArray, NULL);
|
|
memcpy(nArray, waveform, waveformSize);
|
|
env->ReleaseByteArrayElements(jArray, nArray, 0);
|
|
env->CallStaticVoidMethod(
|
|
callbackInfo->visualizer_class,
|
|
fields.midPostNativeEvent,
|
|
callbackInfo->visualizer_ref,
|
|
NATIVE_EVENT_PCM_CAPTURE,
|
|
samplingrate,
|
|
0,
|
|
jArray);
|
|
}
|
|
}
|
|
|
|
if (fftSize != 0 && fft != NULL) {
|
|
jbyteArray jArray = env->NewByteArray(fftSize);
|
|
if (jArray != NULL) {
|
|
jbyte *nArray = env->GetByteArrayElements(jArray, NULL);
|
|
memcpy(nArray, fft, fftSize);
|
|
env->ReleaseByteArrayElements(jArray, nArray, 0);
|
|
env->CallStaticVoidMethod(
|
|
callbackInfo->visualizer_class,
|
|
fields.midPostNativeEvent,
|
|
callbackInfo->visualizer_ref,
|
|
NATIVE_EVENT_FFT_CAPTURE,
|
|
samplingrate,
|
|
0,
|
|
jArray);
|
|
env->DeleteLocalRef(jArray);
|
|
}
|
|
}
|
|
|
|
if (env->ExceptionCheck()) {
|
|
env->ExceptionDescribe();
|
|
env->ExceptionClear();
|
|
}
|
|
}
|
|
|
|
static Visualizer *getVisualizer(JNIEnv* env, jobject thiz)
|
|
{
|
|
Visualizer *v = (Visualizer *)env->GetIntField(
|
|
thiz, fields.fidNativeVisualizer);
|
|
if (v == NULL) {
|
|
jniThrowException(env, "java/lang/IllegalStateException",
|
|
"Unable to retrieve Visualizer pointer");
|
|
}
|
|
return v;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// This function gets some field IDs, which in turn causes class initialization.
|
|
// It is called from a static block in Visualizer, which won't run until the
|
|
// first time an instance of this class is used.
|
|
static void
|
|
android_media_visualizer_native_init(JNIEnv *env)
|
|
{
|
|
|
|
LOGV("android_media_visualizer_native_init");
|
|
|
|
fields.clazzEffect = NULL;
|
|
|
|
// Get the Visualizer 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 Visualizer.%s", "postEventFromNative");
|
|
return;
|
|
}
|
|
|
|
// Get the variables fields
|
|
// nativeTrackInJavaObj
|
|
fields.fidNativeVisualizer = env->GetFieldID(
|
|
fields.clazzEffect,
|
|
"mNativeVisualizer", "I");
|
|
if (fields.fidNativeVisualizer == NULL) {
|
|
LOGE("Can't find Visualizer.%s", "mNativeVisualizer");
|
|
return;
|
|
}
|
|
// fidJniData;
|
|
fields.fidJniData = env->GetFieldID(
|
|
fields.clazzEffect,
|
|
"mJniData", "I");
|
|
if (fields.fidJniData == NULL) {
|
|
LOGE("Can't find Visualizer.%s", "mJniData");
|
|
return;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
static jint
|
|
android_media_visualizer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this,
|
|
jint sessionId, jintArray jId)
|
|
{
|
|
LOGV("android_media_visualizer_native_setup");
|
|
visualizerJniStorage* lpJniStorage = NULL;
|
|
int lStatus = VISUALIZER_ERROR_NO_MEMORY;
|
|
Visualizer* lpVisualizer = NULL;
|
|
jint* nId = NULL;
|
|
|
|
lpJniStorage = new visualizerJniStorage();
|
|
if (lpJniStorage == NULL) {
|
|
LOGE("setup: Error creating JNI Storage");
|
|
goto setup_failure;
|
|
}
|
|
|
|
lpJniStorage->mCallbackData.visualizer_class = (jclass)env->NewGlobalRef(fields.clazzEffect);
|
|
// we use a weak reference so the Visualizer object can be garbage collected.
|
|
lpJniStorage->mCallbackData.visualizer_ref = env->NewGlobalRef(weak_this);
|
|
|
|
LOGV("setup: lpJniStorage: %p visualizer_ref %p visualizer_class %p, &mCallbackData %p",
|
|
lpJniStorage,
|
|
lpJniStorage->mCallbackData.visualizer_ref,
|
|
lpJniStorage->mCallbackData.visualizer_class,
|
|
&lpJniStorage->mCallbackData);
|
|
|
|
if (jId == NULL) {
|
|
LOGE("setup: NULL java array for id pointer");
|
|
lStatus = VISUALIZER_ERROR_BAD_VALUE;
|
|
goto setup_failure;
|
|
}
|
|
|
|
// create the native Visualizer object
|
|
lpVisualizer = new Visualizer(0,
|
|
NULL,
|
|
NULL,
|
|
sessionId);
|
|
if (lpVisualizer == NULL) {
|
|
LOGE("Error creating Visualizer");
|
|
goto setup_failure;
|
|
}
|
|
|
|
lStatus = translateError(lpVisualizer->initCheck());
|
|
if (lStatus != VISUALIZER_SUCCESS && lStatus != VISUALIZER_ERROR_ALREADY_EXISTS) {
|
|
LOGE("Visualizer initCheck failed %d", lStatus);
|
|
goto setup_failure;
|
|
}
|
|
|
|
nId = (jint *) env->GetPrimitiveArrayCritical(jId, NULL);
|
|
if (nId == NULL) {
|
|
LOGE("setup: Error retrieving id pointer");
|
|
lStatus = VISUALIZER_ERROR_BAD_VALUE;
|
|
goto setup_failure;
|
|
}
|
|
nId[0] = lpVisualizer->id();
|
|
env->ReleasePrimitiveArrayCritical(jId, nId, 0);
|
|
nId = NULL;
|
|
|
|
env->SetIntField(thiz, fields.fidNativeVisualizer, (int)lpVisualizer);
|
|
|
|
env->SetIntField(thiz, fields.fidJniData, (int)lpJniStorage);
|
|
|
|
return VISUALIZER_SUCCESS;
|
|
|
|
// failures:
|
|
setup_failure:
|
|
|
|
if (nId != NULL) {
|
|
env->ReleasePrimitiveArrayCritical(jId, nId, 0);
|
|
}
|
|
|
|
if (lpVisualizer) {
|
|
delete lpVisualizer;
|
|
}
|
|
env->SetIntField(thiz, fields.fidNativeVisualizer, 0);
|
|
|
|
if (lpJniStorage) {
|
|
delete lpJniStorage;
|
|
}
|
|
env->SetIntField(thiz, fields.fidJniData, 0);
|
|
|
|
return lStatus;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
static void android_media_visualizer_native_finalize(JNIEnv *env, jobject thiz) {
|
|
LOGV("android_media_visualizer_native_finalize jobject: %x\n", (int)thiz);
|
|
|
|
// delete the Visualizer object
|
|
Visualizer* lpVisualizer = (Visualizer *)env->GetIntField(
|
|
thiz, fields.fidNativeVisualizer);
|
|
if (lpVisualizer) {
|
|
LOGV("deleting Visualizer: %x\n", (int)lpVisualizer);
|
|
delete lpVisualizer;
|
|
}
|
|
|
|
// delete the JNI data
|
|
visualizerJniStorage* lpJniStorage = (visualizerJniStorage *)env->GetIntField(
|
|
thiz, fields.fidJniData);
|
|
if (lpJniStorage) {
|
|
LOGV("deleting pJniStorage: %x\n", (int)lpJniStorage);
|
|
delete lpJniStorage;
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
static void android_media_visualizer_native_release(JNIEnv *env, jobject thiz) {
|
|
|
|
// do everything a call to finalize would
|
|
android_media_visualizer_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.fidNativeVisualizer, 0);
|
|
env->SetIntField(thiz, fields.fidJniData, 0);
|
|
}
|
|
|
|
static jint
|
|
android_media_visualizer_native_setEnabled(JNIEnv *env, jobject thiz, jboolean enabled)
|
|
{
|
|
Visualizer* lpVisualizer = getVisualizer(env, thiz);
|
|
if (lpVisualizer == NULL) {
|
|
return VISUALIZER_ERROR_NO_INIT;
|
|
}
|
|
|
|
return translateError(lpVisualizer->setEnabled(enabled));
|
|
}
|
|
|
|
static jboolean
|
|
android_media_visualizer_native_getEnabled(JNIEnv *env, jobject thiz)
|
|
{
|
|
Visualizer* lpVisualizer = getVisualizer(env, thiz);
|
|
if (lpVisualizer == NULL) {
|
|
return false;
|
|
}
|
|
|
|
return (jboolean)lpVisualizer->getEnabled();
|
|
}
|
|
|
|
static jintArray
|
|
android_media_visualizer_native_getCaptureSizeRange(JNIEnv *env, jobject thiz)
|
|
{
|
|
jintArray jRange = env->NewIntArray(2);
|
|
jint *nRange = env->GetIntArrayElements(jRange, NULL);
|
|
nRange[0] = Visualizer::getMinCaptureSize();
|
|
nRange[1] = Visualizer::getMaxCaptureSize();
|
|
LOGV("getCaptureSizeRange() min %d max %d", nRange[0], nRange[1]);
|
|
env->ReleaseIntArrayElements(jRange, nRange, 0);
|
|
return jRange;
|
|
}
|
|
|
|
static jint
|
|
android_media_visualizer_native_getMaxCaptureRate(JNIEnv *env, jobject thiz)
|
|
{
|
|
return Visualizer::getMaxCaptureRate();
|
|
}
|
|
|
|
static jint
|
|
android_media_visualizer_native_setCaptureSize(JNIEnv *env, jobject thiz, jint size)
|
|
{
|
|
Visualizer* lpVisualizer = getVisualizer(env, thiz);
|
|
if (lpVisualizer == NULL) {
|
|
return VISUALIZER_ERROR_NO_INIT;
|
|
}
|
|
|
|
return translateError(lpVisualizer->setCaptureSize(size));
|
|
}
|
|
|
|
static jint
|
|
android_media_visualizer_native_getCaptureSize(JNIEnv *env, jobject thiz)
|
|
{
|
|
Visualizer* lpVisualizer = getVisualizer(env, thiz);
|
|
if (lpVisualizer == NULL) {
|
|
return -1;
|
|
}
|
|
return lpVisualizer->getCaptureSize();
|
|
}
|
|
|
|
static jint
|
|
android_media_visualizer_native_getSamplingRate(JNIEnv *env, jobject thiz)
|
|
{
|
|
Visualizer* lpVisualizer = getVisualizer(env, thiz);
|
|
if (lpVisualizer == NULL) {
|
|
return -1;
|
|
}
|
|
return lpVisualizer->getSamplingRate();
|
|
}
|
|
|
|
static jint
|
|
android_media_visualizer_native_getWaveForm(JNIEnv *env, jobject thiz, jbyteArray jWaveform)
|
|
{
|
|
Visualizer* lpVisualizer = getVisualizer(env, thiz);
|
|
if (lpVisualizer == NULL) {
|
|
return VISUALIZER_ERROR_NO_INIT;
|
|
}
|
|
|
|
jbyte* nWaveform = (jbyte *) env->GetPrimitiveArrayCritical(jWaveform, NULL);
|
|
if (nWaveform == NULL) {
|
|
return VISUALIZER_ERROR_NO_MEMORY;
|
|
}
|
|
jint status = translateError(lpVisualizer->getWaveForm((uint8_t *)nWaveform));
|
|
|
|
env->ReleasePrimitiveArrayCritical(jWaveform, nWaveform, 0);
|
|
return status;
|
|
}
|
|
|
|
static jint
|
|
android_media_visualizer_native_getFft(JNIEnv *env, jobject thiz, jbyteArray jFft)
|
|
{
|
|
Visualizer* lpVisualizer = getVisualizer(env, thiz);
|
|
if (lpVisualizer == NULL) {
|
|
return VISUALIZER_ERROR_NO_INIT;
|
|
}
|
|
|
|
jbyte* nFft = (jbyte *) env->GetPrimitiveArrayCritical(jFft, NULL);
|
|
if (nFft == NULL) {
|
|
return VISUALIZER_ERROR_NO_MEMORY;
|
|
}
|
|
jint status = translateError(lpVisualizer->getFft((uint8_t *)nFft));
|
|
|
|
env->ReleasePrimitiveArrayCritical(jFft, nFft, 0);
|
|
|
|
return status;
|
|
}
|
|
|
|
static jint
|
|
android_media_setPeriodicCapture(JNIEnv *env, jobject thiz, jint rate, jboolean jWaveform, jboolean jFft)
|
|
{
|
|
Visualizer* lpVisualizer = getVisualizer(env, thiz);
|
|
if (lpVisualizer == NULL) {
|
|
return VISUALIZER_ERROR_NO_INIT;
|
|
}
|
|
visualizerJniStorage* lpJniStorage = (visualizerJniStorage *)env->GetIntField(thiz,
|
|
fields.fidJniData);
|
|
if (lpJniStorage == NULL) {
|
|
return VISUALIZER_ERROR_NO_INIT;
|
|
}
|
|
|
|
LOGV("setPeriodicCapture: rate %d, jWaveform %d jFft %d",
|
|
rate,
|
|
jWaveform,
|
|
jFft);
|
|
|
|
uint32_t flags = Visualizer::CAPTURE_CALL_JAVA;
|
|
if (jWaveform) flags |= Visualizer::CAPTURE_WAVEFORM;
|
|
if (jFft) flags |= Visualizer::CAPTURE_FFT;
|
|
Visualizer::capture_cbk_t cbk = captureCallback;
|
|
if (!jWaveform && !jFft) cbk = NULL;
|
|
|
|
return translateError(lpVisualizer->setCaptureCallBack(cbk,
|
|
&lpJniStorage->mCallbackData,
|
|
flags,
|
|
rate));
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// Dalvik VM type signatures
|
|
static JNINativeMethod gMethods[] = {
|
|
{"native_init", "()V", (void *)android_media_visualizer_native_init},
|
|
{"native_setup", "(Ljava/lang/Object;I[I)I",
|
|
(void *)android_media_visualizer_native_setup},
|
|
{"native_finalize", "()V", (void *)android_media_visualizer_native_finalize},
|
|
{"native_release", "()V", (void *)android_media_visualizer_native_release},
|
|
{"native_setEnabled", "(Z)I", (void *)android_media_visualizer_native_setEnabled},
|
|
{"native_getEnabled", "()Z", (void *)android_media_visualizer_native_getEnabled},
|
|
{"getCaptureSizeRange", "()[I", (void *)android_media_visualizer_native_getCaptureSizeRange},
|
|
{"getMaxCaptureRate", "()I", (void *)android_media_visualizer_native_getMaxCaptureRate},
|
|
{"native_setCaptureSize", "(I)I", (void *)android_media_visualizer_native_setCaptureSize},
|
|
{"native_getCaptureSize", "()I", (void *)android_media_visualizer_native_getCaptureSize},
|
|
{"native_getSamplingRate", "()I", (void *)android_media_visualizer_native_getSamplingRate},
|
|
{"native_getWaveForm", "([B)I", (void *)android_media_visualizer_native_getWaveForm},
|
|
{"native_getFft", "([B)I", (void *)android_media_visualizer_native_getFft},
|
|
{"native_setPeriodicCapture","(IZZ)I",(void *)android_media_setPeriodicCapture},
|
|
};
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
int register_android_media_visualizer(JNIEnv *env)
|
|
{
|
|
return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods));
|
|
}
|
|
|