diff options
author | Eric Laurent <elaurent@google.com> | 2014-05-31 00:49:26 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2014-05-31 00:49:27 +0000 |
commit | a89eb2d8e550b511285ab3b4fb7dec1ed4ca5028 (patch) | |
tree | 2d48237cd68700a09a2084e971eef1daa697202b /core | |
parent | ba23f6da6e40892202199a4b7c5672b92827828d (diff) | |
parent | b69681c894c663e84f2826d9b0c832ceb9b45047 (diff) | |
download | frameworks_base-a89eb2d8e550b511285ab3b4fb7dec1ed4ca5028.zip frameworks_base-a89eb2d8e550b511285ab3b4fb7dec1ed4ca5028.tar.gz frameworks_base-a89eb2d8e550b511285ab3b4fb7dec1ed4ca5028.tar.bz2 |
Merge "AudioSystem: added JNI for routing extensions" into lmp-preview-dev
Diffstat (limited to 'core')
-rw-r--r-- | core/jni/android_media_AudioFormat.h | 54 | ||||
-rw-r--r-- | core/jni/android_media_AudioSystem.cpp | 954 |
2 files changed, 1007 insertions, 1 deletions
diff --git a/core/jni/android_media_AudioFormat.h b/core/jni/android_media_AudioFormat.h index fedb1b2..a2b1ed9 100644 --- a/core/jni/android_media_AudioFormat.h +++ b/core/jni/android_media_AudioFormat.h @@ -23,6 +23,11 @@ #define ENCODING_PCM_16BIT 2 #define ENCODING_PCM_8BIT 3 #define ENCODING_PCM_FLOAT 4 +#define ENCODING_INVALID 0 +#define ENCODING_DEFAULT 1 + +#define CHANNEL_INVALID 0 +#define CHANNEL_OUT_DEFAULT 1 static inline audio_format_t audioFormatToNative(int audioFormat) { @@ -33,9 +38,58 @@ static inline audio_format_t audioFormatToNative(int audioFormat) return AUDIO_FORMAT_PCM_8_BIT; case ENCODING_PCM_FLOAT: return AUDIO_FORMAT_PCM_FLOAT; + case ENCODING_DEFAULT: + return AUDIO_FORMAT_DEFAULT; default: return AUDIO_FORMAT_INVALID; } } +static inline int audioFormatFromNative(audio_format_t nativeFormat) +{ + switch (nativeFormat) { + case AUDIO_FORMAT_PCM_16_BIT: + return ENCODING_PCM_16BIT; + case AUDIO_FORMAT_PCM_8_BIT: + return ENCODING_PCM_8BIT; + case AUDIO_FORMAT_PCM_FLOAT: + return ENCODING_PCM_FLOAT; + case AUDIO_FORMAT_DEFAULT: + return ENCODING_DEFAULT; + default: + return ENCODING_INVALID; + } +} + +static inline audio_channel_mask_t outChannelMaskToNative(int channelMask) +{ + switch (channelMask) { + case CHANNEL_OUT_DEFAULT: + case CHANNEL_INVALID: + return AUDIO_CHANNEL_NONE; + default: + return (audio_channel_mask_t)(channelMask>>2); + } +} + +static inline int outChannelMaskFromNative(audio_channel_mask_t nativeMask) +{ + switch (nativeMask) { + case AUDIO_CHANNEL_NONE: + return CHANNEL_OUT_DEFAULT; + default: + return (int)nativeMask<<2; + } +} + +static inline audio_channel_mask_t inChannelMaskToNative(int channelMask) +{ + return (audio_channel_mask_t)channelMask; +} + +static inline int inChannelMaskFromNative(audio_channel_mask_t nativeMask) +{ + return (int)nativeMask; +} + #endif // ANDROID_MEDIA_AUDIOFORMAT_H diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp index a9a62f8..b6e450d 100644 --- a/core/jni/android_media_AudioSystem.cpp +++ b/core/jni/android_media_AudioSystem.cpp @@ -15,7 +15,7 @@ ** limitations under the License. */ -#define LOG_TAG "AudioSystem" +#define LOG_TAG "AudioSystem-JNI" #include <utils/Log.h> #include <jni.h> @@ -26,6 +26,8 @@ #include <system/audio.h> #include <system/audio_policy.h> +#include "android_media_AudioFormat.h" +#include "android_media_AudioErrors.h" // ---------------------------------------------------------------------------- @@ -33,12 +35,79 @@ using namespace android; static const char* const kClassPathName = "android/media/AudioSystem"; +static jclass gArrayListClass; +static struct { + jmethodID add; +} gArrayListMethods; + +static jclass gAudioHandleClass; +static jmethodID gAudioHandleCstor; +static struct { + jfieldID mId; +} gAudioHandleFields; + +static jclass gAudioPortClass; +static jmethodID gAudioPortCstor; +static struct { + jfieldID mHandle; + jfieldID mRole; + jfieldID mGains; + jfieldID mActiveConfig; + // other fields unused by JNI +} gAudioPortFields; + +static jclass gAudioPortConfigClass; +static jmethodID gAudioPortConfigCstor; +static struct { + jfieldID mPort; + jfieldID mSamplingRate; + jfieldID mChannelMask; + jfieldID mFormat; + jfieldID mGain; +} gAudioPortConfigFields; + +static jclass gAudioDevicePortClass; +static jmethodID gAudioDevicePortCstor; + +static jclass gAudioDevicePortConfigClass; +static jmethodID gAudioDevicePortConfigCstor; + +static jclass gAudioMixPortClass; +static jmethodID gAudioMixPortCstor; + +static jclass gAudioMixPortConfigClass; +static jmethodID gAudioMixPortConfigCstor; + +static jclass gAudioGainClass; +static jmethodID gAudioGainCstor; + +static jclass gAudioGainConfigClass; +static jmethodID gAudioGainConfigCstor; +static struct { + jfieldID mIndex; + jfieldID mMode; + jfieldID mChannelMask; + jfieldID mValues; + jfieldID mRampDurationMs; + // other fields unused by JNI +} gAudioGainConfigFields; + +static jclass gAudioPatchClass; +static jmethodID gAudioPatchCstor; +static struct { + jfieldID mHandle; + // other fields unused by JNI +} gAudioPatchFields; + + enum AudioError { kAudioStatusOk = 0, kAudioStatusError = 1, kAudioStatusMediaServerDied = 100 }; +#define MAX_PORT_GENERATION_SYNC_ATTEMPTS 5 + static int check_AudioSystem_Command(status_t status) { switch (status) { @@ -281,6 +350,801 @@ android_media_AudioSystem_checkAudioFlinger(JNIEnv *env, jobject clazz) return (jint) check_AudioSystem_Command(AudioSystem::checkAudioFlinger()); } + +static bool useInChannelMask(audio_port_type_t type, audio_port_role_t role) +{ + return ((type == AUDIO_PORT_TYPE_DEVICE) && (role == AUDIO_PORT_ROLE_SOURCE)) || + ((type == AUDIO_PORT_TYPE_MIX) && (role == AUDIO_PORT_ROLE_SINK)); +} + +static jint convertAudioPortConfigToNative(JNIEnv *env, + struct audio_port_config *nAudioPortConfig, + const jobject jAudioPortConfig) +{ + jobject jAudioPort = env->GetObjectField(jAudioPortConfig, gAudioPortConfigFields.mPort); + jobject jHandle = env->GetObjectField(jAudioPort, gAudioPortFields.mHandle); + nAudioPortConfig->id = env->GetIntField(jHandle, gAudioHandleFields.mId); + nAudioPortConfig->role = (audio_port_role_t)env->GetIntField(jAudioPort, + gAudioPortFields.mRole); + if (env->IsInstanceOf(jAudioPort, gAudioDevicePortClass)) { + nAudioPortConfig->type = AUDIO_PORT_TYPE_DEVICE; + } else if (env->IsInstanceOf(jAudioPort, gAudioMixPortClass)) { + nAudioPortConfig->type = AUDIO_PORT_TYPE_MIX; + } else { + env->DeleteLocalRef(jAudioPort); + env->DeleteLocalRef(jHandle); + return (jint)AUDIO_JAVA_ERROR; + } + ALOGV("convertAudioPortConfigToNative handle %d role %d type %d", + nAudioPortConfig->id, nAudioPortConfig->role, nAudioPortConfig->type); + + nAudioPortConfig->sample_rate = env->GetIntField(jAudioPortConfig, + gAudioPortConfigFields.mSamplingRate); + + bool useInMask = useInChannelMask(nAudioPortConfig->type, nAudioPortConfig->role); + audio_channel_mask_t nMask; + jint jMask = env->GetIntField(jAudioPortConfig, + gAudioPortConfigFields.mChannelMask); + if (useInMask) { + nMask = inChannelMaskToNative(jMask); + ALOGV("convertAudioPortConfigToNative IN mask java %x native %x", jMask, nMask); + } else { + nMask = outChannelMaskToNative(jMask); + ALOGV("convertAudioPortConfigToNative OUT mask java %x native %x", jMask, nMask); + } + nAudioPortConfig->channel_mask = nMask; + + jint jFormat = env->GetIntField(jAudioPortConfig, gAudioPortConfigFields.mFormat); + audio_format_t nFormat = audioFormatToNative(jFormat); + ALOGV("convertAudioPortConfigToNative format %d native %d", jFormat, nFormat); + nAudioPortConfig->format = nFormat; + jobject jGain = env->GetObjectField(jAudioPortConfig, gAudioPortConfigFields.mGain); + if (jGain != NULL) { + nAudioPortConfig->gain.index = env->GetIntField(jGain, gAudioGainConfigFields.mIndex); + nAudioPortConfig->gain.mode = env->GetIntField(jGain, gAudioGainConfigFields.mMode); + ALOGV("convertAudioPortConfigToNative got gain index %d", nAudioPortConfig->gain.index); + jMask = env->GetIntField(jGain, gAudioGainConfigFields.mChannelMask); + if (useInMask) { + nMask = inChannelMaskToNative(jMask); + ALOGV("convertAudioPortConfigToNative IN mask java %x native %x", jMask, nMask); + } else { + nMask = outChannelMaskToNative(jMask); + ALOGV("convertAudioPortConfigToNative OUT mask java %x native %x", jMask, nMask); + } + + nAudioPortConfig->gain.channel_mask = nMask; + nAudioPortConfig->gain.ramp_duration_ms = env->GetIntField(jGain, + gAudioGainConfigFields.mRampDurationMs); + + jintArray jValues = (jintArray)env->GetObjectField(jGain, gAudioGainConfigFields.mValues); + int *values = env->GetIntArrayElements(jValues, NULL); + size_t size = env->GetArrayLength(jValues); + memcpy(nAudioPortConfig->gain.values, values, size * sizeof(int)); + env->DeleteLocalRef(jValues); + env->DeleteLocalRef(jGain); + } else { + ALOGV("convertAudioPortConfigToNative no gain"); + nAudioPortConfig->gain.index = -1; + } + env->DeleteLocalRef(jAudioPort); + env->DeleteLocalRef(jHandle); + return (jint)AUDIO_JAVA_SUCCESS; +} + +static jint convertAudioPortConfigFromNative(JNIEnv *env, + jobject jAudioPort, + jobject *jAudioPortConfig, + const struct audio_port_config *nAudioPortConfig) +{ + jint jStatus = AUDIO_JAVA_SUCCESS; + jobject jAudioGainConfig = NULL; + jobject jAudioGain = NULL; + jintArray jGainValues; + bool audioportCreated = false; + + ALOGV("convertAudioPortConfigFromNative jAudioPort %p", jAudioPort); + + if (jAudioPort == NULL) { + jobject jHandle = env->NewObject(gAudioHandleClass, gAudioHandleCstor, + nAudioPortConfig->id); + + ALOGV("convertAudioPortConfigFromNative handle %d is a %s", nAudioPortConfig->id, + nAudioPortConfig->type == AUDIO_PORT_TYPE_DEVICE ? "device" : "mix"); + + if (jHandle == NULL) { + return (jint)AUDIO_JAVA_ERROR; + } + // create dummy port and port config objects with just the correct handle + // and configuration data. The actual AudioPortConfig objects will be + // constructed by java code with correct class type (device, mix etc...) + // and reference to AudioPort instance in this client + jAudioPort = env->NewObject(gAudioPortClass, gAudioPortCstor, + jHandle, + 0, + NULL, + NULL, + NULL, + NULL); + env->DeleteLocalRef(jHandle); + if (jAudioPort == NULL) { + return (jint)AUDIO_JAVA_ERROR; + } + ALOGV("convertAudioPortConfigFromNative jAudioPort created for handle %d", + nAudioPortConfig->id); + + audioportCreated = true; + } + + bool useInMask = useInChannelMask(nAudioPortConfig->type, nAudioPortConfig->role); + + audio_channel_mask_t nMask; + jint jMask; + + int gainIndex = nAudioPortConfig->gain.index; + if (gainIndex >= 0) { + ALOGV("convertAudioPortConfigFromNative gain found with index %d mode %x", + gainIndex, nAudioPortConfig->gain.mode); + if (audioportCreated) { + ALOGV("convertAudioPortConfigFromNative creating gain"); + jAudioGain = env->NewObject(gAudioGainClass, gAudioGainCstor, + gainIndex, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0); + if (jAudioGain == NULL) { + ALOGV("convertAudioPortConfigFromNative creating gain FAILED"); + jStatus = (jint)AUDIO_JAVA_ERROR; + goto exit; + } + } else { + ALOGV("convertAudioPortConfigFromNative reading gain from port"); + jobjectArray jGains = (jobjectArray)env->GetObjectField(jAudioPort, + gAudioPortFields.mGains); + if (jGains == NULL) { + ALOGV("convertAudioPortConfigFromNative could not get gains from port"); + jStatus = (jint)AUDIO_JAVA_ERROR; + goto exit; + } + jAudioGain = env->GetObjectArrayElement(jGains, gainIndex); + env->DeleteLocalRef(jGains); + if (jAudioGain == NULL) { + ALOGV("convertAudioPortConfigFromNative could not get gain at index %d", gainIndex); + jStatus = (jint)AUDIO_JAVA_ERROR; + goto exit; + } + } + //TODO: replace popcount by audio utils function mask to count + int numValues = popcount(nAudioPortConfig->gain.channel_mask); + jGainValues = env->NewIntArray(numValues); + if (jGainValues == NULL) { + ALOGV("convertAudioPortConfigFromNative could not create gain values %d", numValues); + jStatus = (jint)AUDIO_JAVA_ERROR; + goto exit; + } + env->SetIntArrayRegion(jGainValues, 0, numValues, + nAudioPortConfig->gain.values); + + nMask = nAudioPortConfig->gain.channel_mask; + if (useInMask) { + jMask = inChannelMaskFromNative(nMask); + ALOGV("convertAudioPortConfigFromNative IN mask java %x native %x", jMask, nMask); + } else { + jMask = outChannelMaskFromNative(nMask); + ALOGV("convertAudioPortConfigFromNative OUT mask java %x native %x", jMask, nMask); + } + + jAudioGainConfig = env->NewObject(gAudioGainConfigClass, + gAudioGainConfigCstor, + gainIndex, + jAudioGain, + nAudioPortConfig->gain.mode, + jMask, + jGainValues, + nAudioPortConfig->gain.ramp_duration_ms); + env->DeleteLocalRef(jGainValues); + if (jAudioGainConfig == NULL) { + ALOGV("convertAudioPortConfigFromNative could not create gain config"); + jStatus = (jint)AUDIO_JAVA_ERROR; + goto exit; + } + } + jclass clazz; + jmethodID methodID; + if (audioportCreated) { + clazz = gAudioPortConfigClass; + methodID = gAudioPortConfigCstor; + ALOGV("convertAudioPortConfigFromNative building a generic port config"); + } else { + if (env->IsInstanceOf(jAudioPort, gAudioDevicePortClass)) { + clazz = gAudioDevicePortConfigClass; + methodID = gAudioDevicePortConfigCstor; + ALOGV("convertAudioPortConfigFromNative building a device config"); + } else if (env->IsInstanceOf(jAudioPort, gAudioMixPortClass)) { + clazz = gAudioMixPortConfigClass; + methodID = gAudioMixPortConfigCstor; + ALOGV("convertAudioPortConfigFromNative building a mix config"); + } else { + jStatus = (jint)AUDIO_JAVA_ERROR; + goto exit; + } + } + nMask = nAudioPortConfig->channel_mask; + if (useInMask) { + jMask = inChannelMaskFromNative(nMask); + ALOGV("convertAudioPortConfigFromNative IN mask java %x native %x", jMask, nMask); + } else { + jMask = outChannelMaskFromNative(nMask); + ALOGV("convertAudioPortConfigFromNative OUT mask java %x native %x", jMask, nMask); + } + + *jAudioPortConfig = env->NewObject(clazz, methodID, + jAudioPort, + nAudioPortConfig->sample_rate, + jMask, + audioFormatFromNative(nAudioPortConfig->format), + jAudioGainConfig); + if (*jAudioPortConfig == NULL) { + ALOGV("convertAudioPortConfigFromNative could not create new port config"); + jStatus = (jint)AUDIO_JAVA_ERROR; + } else { + ALOGV("convertAudioPortConfigFromNative OK"); + } + +exit: + if (audioportCreated) { + env->DeleteLocalRef(jAudioPort); + if (jAudioGain != NULL) { + env->DeleteLocalRef(jAudioGain); + } + } + if (jAudioGainConfig != NULL) { + env->DeleteLocalRef(jAudioGainConfig); + } + return jStatus; +} + + +static jint convertAudioPortFromNative(JNIEnv *env, + jobject *jAudioPort, const struct audio_port *nAudioPort) +{ + jint jStatus = (jint)AUDIO_JAVA_SUCCESS; + jintArray jSamplingRates = NULL; + jintArray jChannelMasks = NULL; + jintArray jFormats = NULL; + jobjectArray jGains = NULL; + jobject jHandle = NULL; + bool useInMask; + + ALOGV("convertAudioPortFromNative id %d role %d type %d", + nAudioPort->id, nAudioPort->role, nAudioPort->type); + + jSamplingRates = env->NewIntArray(nAudioPort->num_sample_rates); + if (jSamplingRates == NULL) { + jStatus = (jint)AUDIO_JAVA_ERROR; + goto exit; + } + if (nAudioPort->num_sample_rates) { + env->SetIntArrayRegion(jSamplingRates, 0, nAudioPort->num_sample_rates, + (jint *)nAudioPort->sample_rates); + } + + jChannelMasks = env->NewIntArray(nAudioPort->num_channel_masks); + if (jChannelMasks == NULL) { + jStatus = (jint)AUDIO_JAVA_ERROR; + goto exit; + } + useInMask = useInChannelMask(nAudioPort->type, nAudioPort->role); + + jint jMask; + for (size_t j = 0; j < nAudioPort->num_channel_masks; j++) { + if (useInMask) { + jMask = inChannelMaskFromNative(nAudioPort->channel_masks[j]); + } else { + jMask = outChannelMaskFromNative(nAudioPort->channel_masks[j]); + } + env->SetIntArrayRegion(jChannelMasks, j, 1, &jMask); + } + + jFormats = env->NewIntArray(nAudioPort->num_formats); + if (jFormats == NULL) { + jStatus = (jint)AUDIO_JAVA_ERROR; + goto exit; + } + for (size_t j = 0; j < nAudioPort->num_formats; j++) { + jint jFormat = audioFormatFromNative(nAudioPort->formats[j]); + env->SetIntArrayRegion(jFormats, j, 1, &jFormat); + } + + jGains = env->NewObjectArray(nAudioPort->num_gains, + gAudioGainClass, NULL); + if (jGains == NULL) { + jStatus = (jint)AUDIO_JAVA_ERROR; + goto exit; + } + for (size_t j = 0; j < nAudioPort->num_gains; j++) { + audio_channel_mask_t nMask = nAudioPort->gains[j].channel_mask; + if (useInMask) { + jMask = inChannelMaskFromNative(nMask); + ALOGV("convertAudioPortConfigFromNative IN mask java %x native %x", jMask, nMask); + } else { + jMask = outChannelMaskFromNative(nMask); + ALOGV("convertAudioPortConfigFromNative OUT mask java %x native %x", jMask, nMask); + } + + jobject jGain = env->NewObject(gAudioGainClass, gAudioGainCstor, + j, + nAudioPort->gains[j].mode, + jMask, + nAudioPort->gains[j].min_value, + nAudioPort->gains[j].max_value, + nAudioPort->gains[j].default_value, + nAudioPort->gains[j].step_value, + nAudioPort->gains[j].min_ramp_ms, + nAudioPort->gains[j].max_ramp_ms); + if (jGain == NULL) { + jStatus = (jint)AUDIO_JAVA_ERROR; + goto exit; + } + env->SetObjectArrayElement(jGains, j, jGain); + env->DeleteLocalRef(jGain); + } + + jHandle = env->NewObject(gAudioHandleClass, gAudioHandleCstor, + nAudioPort->id); + if (jHandle == NULL) { + jStatus = (jint)AUDIO_JAVA_ERROR; + goto exit; + } + + if (nAudioPort->type == AUDIO_PORT_TYPE_DEVICE) { + ALOGV("convertAudioPortFromNative is a device %08x", nAudioPort->ext.device.type); + jstring jAddress = env->NewStringUTF(nAudioPort->ext.device.address); + *jAudioPort = env->NewObject(gAudioDevicePortClass, gAudioDevicePortCstor, + jHandle, jSamplingRates, jChannelMasks, jFormats, jGains, + nAudioPort->ext.device.type, jAddress); + env->DeleteLocalRef(jAddress); + } else if (nAudioPort->type == AUDIO_PORT_TYPE_MIX) { + ALOGV("convertAudioPortFromNative is a mix"); + *jAudioPort = env->NewObject(gAudioMixPortClass, gAudioMixPortCstor, + jHandle, nAudioPort->role, jSamplingRates, jChannelMasks, + jFormats, jGains); + } else { + ALOGE("convertAudioPortFromNative unknown nAudioPort type %d", nAudioPort->type); + jStatus = (jint)AUDIO_JAVA_ERROR; + goto exit; + } + if (*jAudioPort == NULL) { + jStatus = (jint)AUDIO_JAVA_ERROR; + goto exit; + } + + jobject jAudioPortConfig; + jStatus = convertAudioPortConfigFromNative(env, + *jAudioPort, + &jAudioPortConfig, + &nAudioPort->active_config); + if (jStatus != AUDIO_JAVA_SUCCESS) { + return jStatus; + } + + env->SetObjectField(*jAudioPort, gAudioPortFields.mActiveConfig, jAudioPortConfig); + +exit: + if (jSamplingRates != NULL) { + env->DeleteLocalRef(jSamplingRates); + } + if (jChannelMasks != NULL) { + env->DeleteLocalRef(jChannelMasks); + } + if (jFormats != NULL) { + env->DeleteLocalRef(jFormats); + } + if (jGains != NULL) { + env->DeleteLocalRef(jGains); + } + if (jHandle != NULL) { + env->DeleteLocalRef(jHandle); + } + + return jStatus; +} + + +static jint +android_media_AudioSystem_listAudioPorts(JNIEnv *env, jobject clazz, + jobject jPorts, jintArray jGeneration) +{ + ALOGV("listAudioPorts"); + + if (jPorts == NULL) { + ALOGE("listAudioPorts NULL AudioPort ArrayList"); + return (jint)AUDIO_JAVA_BAD_VALUE; + } + if (!env->IsInstanceOf(jPorts, gArrayListClass)) { + ALOGE("listAudioPorts not an arraylist"); + return (jint)AUDIO_JAVA_BAD_VALUE; + } + + if (jGeneration == NULL || env->GetArrayLength(jGeneration) != 1) { + return (jint)AUDIO_JAVA_BAD_VALUE; + } + + status_t status; + unsigned int generation1; + unsigned int generation; + unsigned int numPorts; + jint *nGeneration; + struct audio_port *nPorts = NULL; + int attempts = MAX_PORT_GENERATION_SYNC_ATTEMPTS; + + // get the port count and all the ports until they both return the same generation + do { + if (attempts-- < 0) { + status = TIMED_OUT; + break; + } + + numPorts = 0; + status = AudioSystem::listAudioPorts(AUDIO_PORT_ROLE_NONE, + AUDIO_PORT_TYPE_NONE, + &numPorts, + NULL, + &generation1); + if (status != NO_ERROR || numPorts == 0) { + ALOGE_IF(status != NO_ERROR, "AudioSystem::listAudioPorts error %d", status); + break; + } + nPorts = (struct audio_port *)realloc(nPorts, numPorts * sizeof(struct audio_port)); + + status = AudioSystem::listAudioPorts(AUDIO_PORT_ROLE_NONE, + AUDIO_PORT_TYPE_NONE, + &numPorts, + nPorts, + &generation); + ALOGV("listAudioPorts AudioSystem::listAudioPorts numPorts %d generation %d generation1 %d", + numPorts, generation, generation1); + } while (generation1 != generation && status == NO_ERROR); + + jint jStatus = nativeToJavaStatus(status); + if (jStatus != AUDIO_JAVA_SUCCESS) { + goto exit; + } + + nGeneration = env->GetIntArrayElements(jGeneration, NULL); + if (nGeneration == NULL) { + jStatus = (jint)AUDIO_JAVA_ERROR; + goto exit; + } + nGeneration[0] = generation1; + env->ReleaseIntArrayElements(jGeneration, nGeneration, 0); + + for (size_t i = 0; i < numPorts; i++) { + jobject jAudioPort; + jStatus = convertAudioPortFromNative(env, &jAudioPort, &nPorts[i]); + if (jStatus != AUDIO_JAVA_SUCCESS) { + goto exit; + } + env->CallBooleanMethod(jPorts, gArrayListMethods.add, jAudioPort); + } + +exit: + free(nPorts); + return jStatus; +} + +static int +android_media_AudioSystem_createAudioPatch(JNIEnv *env, jobject clazz, + jobjectArray jPatches, jobjectArray jSources, jobjectArray jSinks) +{ + status_t status; + jint jStatus; + + ALOGV("createAudioPatch"); + if (jPatches == NULL || jSources == NULL || jSinks == NULL) { + return (jint)AUDIO_JAVA_BAD_VALUE; + } + + if (env->GetArrayLength(jPatches) != 1) { + return (jint)AUDIO_JAVA_BAD_VALUE; + } + jint numSources = env->GetArrayLength(jSources); + if (numSources == 0 || numSources > AUDIO_PATCH_PORTS_MAX) { + return (jint)AUDIO_JAVA_BAD_VALUE; + } + + jint numSinks = env->GetArrayLength(jSinks); + if (numSinks == 0 || numSinks > AUDIO_PATCH_PORTS_MAX) { + return (jint)AUDIO_JAVA_BAD_VALUE; + } + + audio_patch_handle_t handle = (audio_patch_handle_t)0; + jobject jPatch = env->GetObjectArrayElement(jPatches, 0); + jobject jPatchHandle = NULL; + if (jPatch != NULL) { + if (!env->IsInstanceOf(jPatch, gAudioPatchClass)) { + return (jint)AUDIO_JAVA_BAD_VALUE; + } + jPatchHandle = env->GetObjectField(jPatch, gAudioPatchFields.mHandle); + handle = (audio_patch_handle_t)env->GetIntField(jPatchHandle, gAudioHandleFields.mId); + } + + struct audio_patch nPatch; + + nPatch.id = handle; + nPatch.num_sources = 0; + nPatch.num_sinks = 0; + jobject jSource = NULL; + jobject jSink = NULL; + + for (jint i = 0; i < numSources; i++) { + jSource = env->GetObjectArrayElement(jSources, i); + if (!env->IsInstanceOf(jSource, gAudioPortConfigClass)) { + jStatus = (jint)AUDIO_JAVA_BAD_VALUE; + goto exit; + } + jStatus = convertAudioPortConfigToNative(env, &nPatch.sources[i], jSource); + env->DeleteLocalRef(jSource); + jSource = NULL; + if (jStatus != AUDIO_JAVA_SUCCESS) { + goto exit; + } + nPatch.num_sources++; + } + + for (jint i = 0; i < numSinks; i++) { + jSink = env->GetObjectArrayElement(jSinks, i); + if (!env->IsInstanceOf(jSink, gAudioPortConfigClass)) { + jStatus = (jint)AUDIO_JAVA_BAD_VALUE; + goto exit; + } + jStatus = convertAudioPortConfigToNative(env, &nPatch.sinks[i], jSink); + env->DeleteLocalRef(jSink); + jSink = NULL; + if (jStatus != AUDIO_JAVA_SUCCESS) { + goto exit; + } + nPatch.num_sinks++; + } + + ALOGV("AudioSystem::createAudioPatch"); + status = AudioSystem::createAudioPatch(&nPatch, &handle); + ALOGV("AudioSystem::createAudioPatch() returned %d hande %d", status, handle); + + jStatus = nativeToJavaStatus(status); + if (jStatus != AUDIO_JAVA_SUCCESS) { + goto exit; + } + + if (jPatchHandle == NULL) { + jPatchHandle = env->NewObject(gAudioHandleClass, gAudioHandleCstor, + handle); + if (jPatchHandle == NULL) { + jStatus = (jint)AUDIO_JAVA_ERROR; + goto exit; + } + jPatch = env->NewObject(gAudioPatchClass, gAudioPatchCstor, jPatchHandle, jSources, jSinks); + if (jPatch == NULL) { + jStatus = (jint)AUDIO_JAVA_ERROR; + goto exit; + } + env->SetObjectArrayElement(jPatches, 0, jPatch); + } else { + env->SetIntField(jPatchHandle, gAudioHandleFields.mId, handle); + } + +exit: + if (jPatchHandle != NULL) { + env->DeleteLocalRef(jPatchHandle); + } + if (jPatch != NULL) { + env->DeleteLocalRef(jPatch); + } + if (jSource != NULL) { + env->DeleteLocalRef(jSource); + } + if (jSink != NULL) { + env->DeleteLocalRef(jSink); + } + return jStatus; +} + +static int +android_media_AudioSystem_releaseAudioPatch(JNIEnv *env, jobject clazz, + jobject jPatch) +{ + ALOGV("releaseAudioPatch"); + if (jPatch == NULL) { + return (jint)AUDIO_JAVA_BAD_VALUE; + } + + audio_patch_handle_t handle = (audio_patch_handle_t)0; + jobject jPatchHandle = NULL; + if (!env->IsInstanceOf(jPatch, gAudioPatchClass)) { + return (jint)AUDIO_JAVA_BAD_VALUE; + } + jPatchHandle = env->GetObjectField(jPatch, gAudioPatchFields.mHandle); + handle = (audio_patch_handle_t)env->GetIntField(jPatchHandle, gAudioHandleFields.mId); + env->DeleteLocalRef(jPatchHandle); + + ALOGV("AudioSystem::releaseAudioPatch"); + status_t status = AudioSystem::releaseAudioPatch(handle); + ALOGV("AudioSystem::releaseAudioPatch() returned %d", status); + jint jStatus = nativeToJavaStatus(status); + return status; +} + +static jint +android_media_AudioSystem_listAudioPatches(JNIEnv *env, jobject clazz, + jobject jPatches, jintArray jGeneration) +{ + ALOGV("listAudioPatches"); + if (jPatches == NULL) { + ALOGE("listAudioPatches NULL AudioPatch ArrayList"); + return (jint)AUDIO_JAVA_BAD_VALUE; + } + if (!env->IsInstanceOf(jPatches, gArrayListClass)) { + ALOGE("listAudioPatches not an arraylist"); + return (jint)AUDIO_JAVA_BAD_VALUE; + } + + if (jGeneration == NULL || env->GetArrayLength(jGeneration) != 1) { + return (jint)AUDIO_JAVA_BAD_VALUE; + } + + status_t status; + unsigned int generation1; + unsigned int generation; + unsigned int numPatches; + jint *nGeneration; + struct audio_patch *nPatches = NULL; + jobjectArray jSources = NULL; + jobject jSource = NULL; + jobjectArray jSinks = NULL; + jobject jSink = NULL; + jobject jPatch = NULL; + int attempts = MAX_PORT_GENERATION_SYNC_ATTEMPTS; + + // get the patch count and all the patches until they both return the same generation + do { + if (attempts-- < 0) { + status = TIMED_OUT; + break; + } + + numPatches = 0; + status = AudioSystem::listAudioPatches(&numPatches, + NULL, + &generation1); + if (status != NO_ERROR || numPatches == 0) { + ALOGE_IF(status != NO_ERROR, "listAudioPatches AudioSystem::listAudioPatches error %d", + status); + break; + } + nPatches = (struct audio_patch *)realloc(nPatches, numPatches * sizeof(struct audio_patch)); + + status = AudioSystem::listAudioPatches(&numPatches, + nPatches, + &generation); + ALOGV("listAudioPatches AudioSystem::listAudioPatches numPatches %d generation %d generation1 %d", + numPatches, generation, generation1); + + } while (generation1 != generation && status == NO_ERROR); + + jint jStatus = nativeToJavaStatus(status); + if (jStatus != AUDIO_JAVA_SUCCESS) { + goto exit; + } + + nGeneration = env->GetIntArrayElements(jGeneration, NULL); + if (nGeneration == NULL) { + jStatus = AUDIO_JAVA_ERROR; + goto exit; + } + nGeneration[0] = generation1; + env->ReleaseIntArrayElements(jGeneration, nGeneration, 0); + + for (size_t i = 0; i < numPatches; i++) { + jobject patchHandle = env->NewObject(gAudioHandleClass, gAudioHandleCstor, + nPatches[i].id); + if (patchHandle == NULL) { + jStatus = AUDIO_JAVA_ERROR; + goto exit; + } + ALOGV("listAudioPatches patch %d num_sources %d num_sinks %d", + i, nPatches[i].num_sources, nPatches[i].num_sinks); + + env->SetIntField(patchHandle, gAudioHandleFields.mId, nPatches[i].id); + + // load sources + jSources = env->NewObjectArray(nPatches[i].num_sources, + gAudioPortConfigClass, NULL); + if (jSources == NULL) { + jStatus = AUDIO_JAVA_ERROR; + goto exit; + } + + for (size_t j = 0; j < nPatches[i].num_sources; j++) { + jStatus = convertAudioPortConfigFromNative(env, + NULL, + &jSource, + &nPatches[i].sources[j]); + if (jStatus != AUDIO_JAVA_SUCCESS) { + goto exit; + } + env->SetObjectArrayElement(jSources, j, jSource); + env->DeleteLocalRef(jSource); + jSource = NULL; + ALOGV("listAudioPatches patch %d source %d is a %s handle %d", + i, j, + nPatches[i].sources[j].type == AUDIO_PORT_TYPE_DEVICE ? "device" : "mix", + nPatches[i].sources[j].id); + } + // load sinks + jSinks = env->NewObjectArray(nPatches[i].num_sinks, + gAudioPortConfigClass, NULL); + if (jSinks == NULL) { + jStatus = AUDIO_JAVA_ERROR; + goto exit; + } + + for (size_t j = 0; j < nPatches[i].num_sinks; j++) { + jStatus = convertAudioPortConfigFromNative(env, + NULL, + &jSink, + &nPatches[i].sinks[j]); + + if (jStatus != AUDIO_JAVA_SUCCESS) { + goto exit; + } + env->SetObjectArrayElement(jSinks, j, jSink); + env->DeleteLocalRef(jSink); + jSink = NULL; + ALOGV("listAudioPatches patch %d sink %d is a %s handle %d", + i, j, + nPatches[i].sinks[j].type == AUDIO_PORT_TYPE_DEVICE ? "device" : "mix", + nPatches[i].sinks[j].id); + } + + jPatch = env->NewObject(gAudioPatchClass, gAudioPatchCstor, + patchHandle, jSources, jSinks); + env->DeleteLocalRef(jSources); + jSources = NULL; + env->DeleteLocalRef(jSinks); + jSinks = NULL; + if (jPatch == NULL) { + jStatus = AUDIO_JAVA_ERROR; + goto exit; + } + env->CallBooleanMethod(jPatches, gArrayListMethods.add, jPatch); + env->DeleteLocalRef(jPatch); + jPatch = NULL; + } + +exit: + if (jSources != NULL) { + env->DeleteLocalRef(jSources); + } + if (jSource != NULL) { + env->DeleteLocalRef(jSource); + } + if (jSinks != NULL) { + env->DeleteLocalRef(jSinks); + } + if (jSink != NULL) { + env->DeleteLocalRef(jSink); + } + if (jPatch != NULL) { + env->DeleteLocalRef(jPatch); + } + free(nPatches); + return jStatus; +} + // ---------------------------------------------------------------------------- static JNINativeMethod gMethods[] = { @@ -309,10 +1173,98 @@ static JNINativeMethod gMethods[] = { {"getOutputLatency", "(I)I", (void *)android_media_AudioSystem_getOutputLatency}, {"setLowRamDevice", "(Z)I", (void *)android_media_AudioSystem_setLowRamDevice}, {"checkAudioFlinger", "()I", (void *)android_media_AudioSystem_checkAudioFlinger}, + {"listAudioPorts", "(Ljava/util/ArrayList;[I)I", + (void *)android_media_AudioSystem_listAudioPorts}, + {"createAudioPatch", "([Landroid/media/AudioPatch;[Landroid/media/AudioPortConfig;[Landroid/media/AudioPortConfig;)I", + (void *)android_media_AudioSystem_createAudioPatch}, + {"releaseAudioPatch", "(Landroid/media/AudioPatch;)I", + (void *)android_media_AudioSystem_releaseAudioPatch}, + {"listAudioPatches", "(Ljava/util/ArrayList;[I)I", + (void *)android_media_AudioSystem_listAudioPatches}, }; + int register_android_media_AudioSystem(JNIEnv *env) { + + jclass arrayListClass = env->FindClass("java/util/ArrayList"); + gArrayListClass = (jclass) env->NewGlobalRef(arrayListClass); + gArrayListMethods.add = env->GetMethodID(arrayListClass, "add", "(Ljava/lang/Object;)Z"); + + jclass audioHandleClass = env->FindClass("android/media/AudioHandle"); + gAudioHandleClass = (jclass) env->NewGlobalRef(audioHandleClass); + gAudioHandleCstor = env->GetMethodID(audioHandleClass, "<init>", "(I)V"); + gAudioHandleFields.mId = env->GetFieldID(audioHandleClass, "mId", "I"); + + jclass audioPortClass = env->FindClass("android/media/AudioPort"); + gAudioPortClass = (jclass) env->NewGlobalRef(audioPortClass); + gAudioPortCstor = env->GetMethodID(audioPortClass, "<init>", + "(Landroid/media/AudioHandle;I[I[I[I[Landroid/media/AudioGain;)V"); + gAudioPortFields.mHandle = env->GetFieldID(audioPortClass, "mHandle", + "Landroid/media/AudioHandle;"); + gAudioPortFields.mRole = env->GetFieldID(audioPortClass, "mRole", "I"); + gAudioPortFields.mGains = env->GetFieldID(audioPortClass, "mGains", + "[Landroid/media/AudioGain;"); + gAudioPortFields.mActiveConfig = env->GetFieldID(audioPortClass, "mActiveConfig", + "Landroid/media/AudioPortConfig;"); + + jclass audioPortConfigClass = env->FindClass("android/media/AudioPortConfig"); + gAudioPortConfigClass = (jclass) env->NewGlobalRef(audioPortConfigClass); + gAudioPortConfigCstor = env->GetMethodID(audioPortConfigClass, "<init>", + "(Landroid/media/AudioPort;IIILandroid/media/AudioGainConfig;)V"); + gAudioPortConfigFields.mPort = env->GetFieldID(audioPortConfigClass, "mPort", + "Landroid/media/AudioPort;"); + gAudioPortConfigFields.mSamplingRate = env->GetFieldID(audioPortConfigClass, + "mSamplingRate", "I"); + gAudioPortConfigFields.mChannelMask = env->GetFieldID(audioPortConfigClass, + "mChannelMask", "I"); + gAudioPortConfigFields.mFormat = env->GetFieldID(audioPortConfigClass, "mFormat", "I"); + gAudioPortConfigFields.mGain = env->GetFieldID(audioPortConfigClass, "mGain", + "Landroid/media/AudioGainConfig;"); + + jclass audioDevicePortConfigClass = env->FindClass("android/media/AudioDevicePortConfig"); + gAudioDevicePortConfigClass = (jclass) env->NewGlobalRef(audioDevicePortConfigClass); + gAudioDevicePortConfigCstor = env->GetMethodID(audioDevicePortConfigClass, "<init>", + "(Landroid/media/AudioDevicePort;IIILandroid/media/AudioGainConfig;)V"); + + jclass audioMixPortConfigClass = env->FindClass("android/media/AudioMixPortConfig"); + gAudioMixPortConfigClass = (jclass) env->NewGlobalRef(audioMixPortConfigClass); + gAudioMixPortConfigCstor = env->GetMethodID(audioMixPortConfigClass, "<init>", + "(Landroid/media/AudioMixPort;IIILandroid/media/AudioGainConfig;)V"); + + jclass audioDevicePortClass = env->FindClass("android/media/AudioDevicePort"); + gAudioDevicePortClass = (jclass) env->NewGlobalRef(audioDevicePortClass); + gAudioDevicePortCstor = env->GetMethodID(audioDevicePortClass, "<init>", + "(Landroid/media/AudioHandle;[I[I[I[Landroid/media/AudioGain;ILjava/lang/String;)V"); + + jclass audioMixPortClass = env->FindClass("android/media/AudioMixPort"); + gAudioMixPortClass = (jclass) env->NewGlobalRef(audioMixPortClass); + gAudioMixPortCstor = env->GetMethodID(audioMixPortClass, "<init>", + "(Landroid/media/AudioHandle;I[I[I[I[Landroid/media/AudioGain;)V"); + + jclass audioGainClass = env->FindClass("android/media/AudioGain"); + gAudioGainClass = (jclass) env->NewGlobalRef(audioGainClass); + gAudioGainCstor = env->GetMethodID(audioGainClass, "<init>", "(IIIIIIIII)V"); + + jclass audioGainConfigClass = env->FindClass("android/media/AudioGainConfig"); + gAudioGainConfigClass = (jclass) env->NewGlobalRef(audioGainConfigClass); + gAudioGainConfigCstor = env->GetMethodID(audioGainConfigClass, "<init>", + "(ILandroid/media/AudioGain;II[II)V"); + gAudioGainConfigFields.mIndex = env->GetFieldID(gAudioGainConfigClass, "mIndex", "I"); + gAudioGainConfigFields.mMode = env->GetFieldID(audioGainConfigClass, "mMode", "I"); + gAudioGainConfigFields.mChannelMask = env->GetFieldID(audioGainConfigClass, "mChannelMask", + "I"); + gAudioGainConfigFields.mValues = env->GetFieldID(audioGainConfigClass, "mValues", "[I"); + gAudioGainConfigFields.mRampDurationMs = env->GetFieldID(audioGainConfigClass, + "mRampDurationMs", "I"); + + jclass audioPatchClass = env->FindClass("android/media/AudioPatch"); + gAudioPatchClass = (jclass) env->NewGlobalRef(audioPatchClass); + gAudioPatchCstor = env->GetMethodID(audioPatchClass, "<init>", +"(Landroid/media/AudioHandle;[Landroid/media/AudioPortConfig;[Landroid/media/AudioPortConfig;)V"); + gAudioPatchFields.mHandle = env->GetFieldID(audioPatchClass, "mHandle", + "Landroid/media/AudioHandle;"); + AudioSystem::setErrorCallback(android_media_AudioSystem_error_callback); return AndroidRuntime::registerNativeMethods(env, |