From 44561f0a792a95cc7540162fa301309aa7c77ddb Mon Sep 17 00:00:00 2001 From: jiabin Date: Tue, 24 Nov 2020 11:32:44 -0800 Subject: [PATCH] Introduce AudioProfile to describe the capabilities of audio ports. For an audio port, the capabilities are currently described by flat arrays of formats, channel masks and sample rates. This doesn't expose the relationship between supported formats and channel masks/sample rates. In that case, AudioProfile is introduced which contains the audio format and its associated sample rates and channel masks. Test: atest AudioManagerTest Bug: 25994457 Bug: 160352965 Change-Id: Ic99713e32a1585875001ee096eb27d6c9c5cb64f --- core/api/current.txt | 8 + core/jni/android_media_AudioSystem.cpp | 257 ++++++++---------- media/java/android/media/AudioDeviceInfo.java | 8 + media/java/android/media/AudioDevicePort.java | 13 + media/java/android/media/AudioMixPort.java | 8 + media/java/android/media/AudioPort.java | 49 +++- media/java/android/media/AudioProfile.java | 68 +++++ 7 files changed, 270 insertions(+), 141 deletions(-) create mode 100644 media/java/android/media/AudioProfile.java diff --git a/core/api/current.txt b/core/api/current.txt index 9f7f3917a11f4..febdd1047d35f 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -24288,6 +24288,7 @@ package android.media { public final class AudioDeviceInfo { method @NonNull public String getAddress(); + method @NonNull public java.util.List getAudioProfiles(); method @NonNull public int[] getChannelCounts(); method @NonNull public int[] getChannelIndexMasks(); method @NonNull public int[] getChannelMasks(); @@ -24705,6 +24706,13 @@ package android.media { method @NonNull public android.media.AudioPresentation.Builder setProgramId(int); } + public class AudioProfile { + method @NonNull public int[] getChannelIndexMasks(); + method @NonNull public int[] getChannelMasks(); + method public int getFormat(); + method @NonNull public int[] getSampleRates(); + } + public class AudioRecord implements android.media.AudioRecordingMonitor android.media.AudioRouting android.media.MicrophoneDirection { ctor public AudioRecord(int, int, int, int, int) throws java.lang.IllegalArgumentException; method public void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler); diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp index 1ca45fe9f70b1..b1b0284e5295a 100644 --- a/core/jni/android_media_AudioSystem.cpp +++ b/core/jni/android_media_AudioSystem.cpp @@ -48,8 +48,9 @@ static const char* const kClassPathName = "android/media/AudioSystem"; static jclass gArrayListClass; static struct { - jmethodID add; - jmethodID toArray; + jmethodID cstor; + jmethodID add; + jmethodID toArray; } gArrayListMethods; static jclass gBooleanClass; @@ -185,6 +186,9 @@ jmethodID gMidAudioRecordRoutingProxy_release; jclass gClsAudioTrackRoutingProxy; jclass gClsAudioRecordRoutingProxy; +jclass gAudioProfileClass; +jmethodID gAudioProfileCstor; + static Mutex gLock; enum AudioError { @@ -807,13 +811,6 @@ 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 void convertAudioGainConfigToNative(JNIEnv *env, struct audio_gain_config *nAudioGainConfig, const jobject jAudioGainConfig, @@ -873,7 +870,7 @@ static jint convertAudioPortConfigToNative(JNIEnv *env, configMask |= AUDIO_PORT_CONFIG_SAMPLE_RATE; } - bool useInMask = useInChannelMask(nAudioPortConfig->type, nAudioPortConfig->role); + bool useInMask = audio_port_config_has_input_direction(nAudioPortConfig); audio_channel_mask_t nMask; jint jMask = env->GetIntField(jAudioPortConfig, gAudioPortConfigFields.mChannelMask); @@ -1000,7 +997,7 @@ static jint convertAudioPortConfigFromNative(JNIEnv *env, audioportCreated = true; } - bool useInMask = useInChannelMask(nAudioPortConfig->type, nAudioPortConfig->role); + bool useInMask = audio_port_config_has_input_direction(nAudioPortConfig); audio_channel_mask_t nMask; jint jMask; @@ -1137,15 +1134,6 @@ exit: return jStatus; } -static bool hasFormat(int* formats, size_t size, int format) { - for (size_t index = 0; index < size; index++) { - if (formats[index] == format) { - return true; // found - } - } - return false; // not found -} - // TODO: pull out to separate file template static constexpr size_t array_size(const T (&)[N]) { @@ -1167,43 +1155,46 @@ static jintArray convertEncapsulationInfoFromNative(JNIEnv *env, uint32_t encaps return result; } -static jint convertAudioPortFromNative(JNIEnv *env, - jobject *jAudioPort, const struct audio_port *nAudioPort) -{ +static bool isAudioPortArrayCountOutOfBounds(const struct audio_port_v7 *nAudioPort, + std::stringstream &ss) { + ss << " num_audio_profiles " << nAudioPort->num_audio_profiles << " num_gains " + << nAudioPort->num_gains; + if (nAudioPort->num_audio_profiles > array_size(nAudioPort->audio_profiles) || + nAudioPort->num_gains > array_size(nAudioPort->gains)) { + return true; + } + for (size_t i = 0; i < nAudioPort->num_audio_profiles; ++i) { + ss << " (" << i << ") audio profile," + << " num_sample_rates " << nAudioPort->audio_profiles[i].num_sample_rates + << " num_channel_masks " << nAudioPort->audio_profiles[i].num_channel_masks; + if (nAudioPort->audio_profiles[i].num_sample_rates > + array_size(nAudioPort->audio_profiles[i].sample_rates) || + nAudioPort->audio_profiles[i].num_channel_masks > + array_size(nAudioPort->audio_profiles[i].channel_masks)) { + return true; + } + } + return false; +} + +static jint convertAudioPortFromNative(JNIEnv *env, jobject *jAudioPort, + const struct audio_port_v7 *nAudioPort) { jint jStatus = (jint)AUDIO_JAVA_SUCCESS; - jintArray jSamplingRates = NULL; - jintArray jChannelMasks = NULL; - jintArray jChannelIndexMasks = NULL; jintArray jEncapsulationModes = NULL; jintArray jEncapsulationMetadataTypes = NULL; - int* cFormats = NULL; - jintArray jFormats = NULL; jobjectArray jGains = NULL; jobject jHandle = NULL; jobject jAudioPortConfig = NULL; jstring jDeviceName = NULL; + jobject jAudioProfiles = NULL; bool useInMask; - size_t numPositionMasks = 0; - size_t numIndexMasks = 0; - size_t numUniqueFormats = 0; ALOGV("convertAudioPortFromNative id %d role %d type %d name %s", nAudioPort->id, nAudioPort->role, nAudioPort->type, nAudioPort->name); // Verify audio port array count info. - if (nAudioPort->num_sample_rates > array_size(nAudioPort->sample_rates) - || nAudioPort->num_channel_masks > array_size(nAudioPort->channel_masks) - || nAudioPort->num_formats > array_size(nAudioPort->formats) - || nAudioPort->num_gains > array_size(nAudioPort->gains)) { - - std::stringstream ss; - ss << "convertAudioPortFromNative array count out of bounds:" - << " num_sample_rates " << nAudioPort->num_sample_rates - << " num_channel_masks " << nAudioPort->num_channel_masks - << " num_formats " << nAudioPort->num_formats - << " num_gains " << nAudioPort->num_gains - ; - std::string s = ss.str(); + if (std::stringstream ss; isAudioPortArrayCountOutOfBounds(nAudioPort, ss)) { + std::string s = "convertAudioPortFromNative array count out of bounds:" + ss.str(); // Prefer to log through Java wtf instead of native ALOGE. ScopedLocalRef jLogClass(env, env->FindClass("android/util/Log")); @@ -1222,69 +1213,68 @@ static jint convertAudioPortFromNative(JNIEnv *env, goto exit; } - jSamplingRates = env->NewIntArray(nAudioPort->num_sample_rates); - if (jSamplingRates == NULL) { + useInMask = audio_has_input_direction(nAudioPort->type, nAudioPort->role); + jAudioProfiles = env->NewObject(gArrayListClass, gArrayListMethods.cstor); + if (jAudioProfiles == nullptr) { jStatus = (jint)AUDIO_JAVA_ERROR; goto exit; } - if (nAudioPort->num_sample_rates) { - env->SetIntArrayRegion(jSamplingRates, 0, nAudioPort->num_sample_rates, - (jint *)nAudioPort->sample_rates); - } - - // count up how many masks are positional and indexed - for(size_t index = 0; index < nAudioPort->num_channel_masks; index++) { - const audio_channel_mask_t mask = nAudioPort->channel_masks[index]; - if (audio_channel_mask_get_representation(mask) == AUDIO_CHANNEL_REPRESENTATION_INDEX) { - numIndexMasks++; - } else { - numPositionMasks++; - } - } - - jChannelMasks = env->NewIntArray(numPositionMasks); - if (jChannelMasks == NULL) { - jStatus = (jint)AUDIO_JAVA_ERROR; - goto exit; - } - jChannelIndexMasks = env->NewIntArray(numIndexMasks); - if (jChannelIndexMasks == NULL) { - jStatus = (jint)AUDIO_JAVA_ERROR; - goto exit; - } - useInMask = useInChannelMask(nAudioPort->type, nAudioPort->role); - - // put the masks in the output arrays - for (size_t maskIndex = 0, posMaskIndex = 0, indexedMaskIndex = 0; - maskIndex < nAudioPort->num_channel_masks; maskIndex++) { - const audio_channel_mask_t mask = nAudioPort->channel_masks[maskIndex]; - if (audio_channel_mask_get_representation(mask) == AUDIO_CHANNEL_REPRESENTATION_INDEX) { - jint jMask = audio_channel_mask_get_bits(mask); - env->SetIntArrayRegion(jChannelIndexMasks, indexedMaskIndex++, 1, &jMask); - } else { - jint jMask = useInMask ? inChannelMaskFromNative(mask) - : outChannelMaskFromNative(mask); - env->SetIntArrayRegion(jChannelMasks, posMaskIndex++, 1, &jMask); - } - } - - // formats - if (nAudioPort->num_formats != 0) { - cFormats = new int[nAudioPort->num_formats]; - for (size_t index = 0; index < nAudioPort->num_formats; index++) { - int format = audioFormatFromNative(nAudioPort->formats[index]); - if (!hasFormat(cFormats, numUniqueFormats, format)) { - cFormats[numUniqueFormats++] = format; + for (size_t i = 0; i < nAudioPort->num_audio_profiles; ++i) { + size_t numPositionMasks = 0; + size_t numIndexMasks = 0; + // count up how many masks are positional and indexed + for (size_t index = 0; index < nAudioPort->audio_profiles[i].num_channel_masks; index++) { + const audio_channel_mask_t mask = nAudioPort->audio_profiles[i].channel_masks[index]; + if (audio_channel_mask_get_representation(mask) == AUDIO_CHANNEL_REPRESENTATION_INDEX) { + numIndexMasks++; + } else { + numPositionMasks++; } } - } - jFormats = env->NewIntArray(numUniqueFormats); - if (jFormats == NULL) { - jStatus = (jint)AUDIO_JAVA_ERROR; - goto exit; - } - if (numUniqueFormats != 0) { - env->SetIntArrayRegion(jFormats, 0, numUniqueFormats, cFormats); + + ScopedLocalRef jSamplingRates(env, + env->NewIntArray(nAudioPort->audio_profiles[i] + .num_sample_rates)); + ScopedLocalRef jChannelMasks(env, env->NewIntArray(numPositionMasks)); + ScopedLocalRef jChannelIndexMasks(env, env->NewIntArray(numIndexMasks)); + if (!jSamplingRates.get() || !jChannelMasks.get() || !jChannelIndexMasks.get()) { + jStatus = (jint)AUDIO_JAVA_ERROR; + goto exit; + } + + if (nAudioPort->audio_profiles[i].num_sample_rates) { + env->SetIntArrayRegion(jSamplingRates.get(), 0 /*start*/, + nAudioPort->audio_profiles[i].num_sample_rates, + (jint *)nAudioPort->audio_profiles[i].sample_rates); + } + + // put the masks in the output arrays + for (size_t maskIndex = 0, posMaskIndex = 0, indexedMaskIndex = 0; + maskIndex < nAudioPort->audio_profiles[i].num_channel_masks; maskIndex++) { + const audio_channel_mask_t mask = + nAudioPort->audio_profiles[i].channel_masks[maskIndex]; + if (audio_channel_mask_get_representation(mask) == AUDIO_CHANNEL_REPRESENTATION_INDEX) { + jint jMask = audio_channel_mask_get_bits(mask); + env->SetIntArrayRegion(jChannelIndexMasks.get(), indexedMaskIndex++, 1, &jMask); + } else { + jint jMask = + useInMask ? inChannelMaskFromNative(mask) : outChannelMaskFromNative(mask); + env->SetIntArrayRegion(jChannelMasks.get(), posMaskIndex++, 1, &jMask); + } + } + + ScopedLocalRef + jAudioProfile(env, + env->NewObject(gAudioProfileClass, gAudioProfileCstor, + audioFormatFromNative( + nAudioPort->audio_profiles[i].format), + jSamplingRates.get(), jChannelMasks.get(), + jChannelIndexMasks.get())); + if (jAudioProfile == nullptr) { + jStatus = (jint)AUDIO_JAVA_ERROR; + goto exit; + } + env->CallBooleanMethod(jAudioProfiles, gArrayListMethods.add, jAudioProfile.get()); } // gains @@ -1342,18 +1332,16 @@ static jint convertAudioPortFromNative(JNIEnv *env, convertEncapsulationInfoFromNative(env, nAudioPort->ext.device .encapsulation_metadata_types); - *jAudioPort = env->NewObject(gAudioDevicePortClass, gAudioDevicePortCstor, jHandle, - jDeviceName, jSamplingRates, jChannelMasks, jChannelIndexMasks, - jFormats, jGains, nAudioPort->ext.device.type, jAddress, - jEncapsulationModes, jEncapsulationMetadataTypes); + *jAudioPort = + env->NewObject(gAudioDevicePortClass, gAudioDevicePortCstor, jHandle, jDeviceName, + jAudioProfiles, jGains, nAudioPort->ext.device.type, jAddress, + jEncapsulationModes, jEncapsulationMetadataTypes); env->DeleteLocalRef(jAddress); } else if (nAudioPort->type == AUDIO_PORT_TYPE_MIX) { ALOGV("convertAudioPortFromNative is a mix"); - *jAudioPort = env->NewObject(gAudioMixPortClass, gAudioMixPortCstor, - jHandle, nAudioPort->ext.mix.handle, - nAudioPort->role, jDeviceName, - jSamplingRates, jChannelMasks, jChannelIndexMasks, - jFormats, jGains); + *jAudioPort = env->NewObject(gAudioMixPortClass, gAudioMixPortCstor, jHandle, + nAudioPort->ext.mix.handle, nAudioPort->role, jDeviceName, + jAudioProfiles, jGains); } else { ALOGE("convertAudioPortFromNative unknown nAudioPort type %d", nAudioPort->type); jStatus = (jint)AUDIO_JAVA_ERROR; @@ -1378,26 +1366,14 @@ exit: if (jDeviceName != NULL) { env->DeleteLocalRef(jDeviceName); } - if (jSamplingRates != NULL) { - env->DeleteLocalRef(jSamplingRates); - } - if (jChannelMasks != NULL) { - env->DeleteLocalRef(jChannelMasks); - } - if (jChannelIndexMasks != NULL) { - env->DeleteLocalRef(jChannelIndexMasks); - } if (jEncapsulationModes != NULL) { env->DeleteLocalRef(jEncapsulationModes); } if (jEncapsulationMetadataTypes != NULL) { env->DeleteLocalRef(jEncapsulationMetadataTypes); } - if (cFormats != NULL) { - delete[] cFormats; - } - if (jFormats != NULL) { - env->DeleteLocalRef(jFormats); + if (jAudioProfiles != NULL) { + env->DeleteLocalRef(jAudioProfiles); } if (jGains != NULL) { env->DeleteLocalRef(jGains); @@ -1436,7 +1412,7 @@ android_media_AudioSystem_listAudioPorts(JNIEnv *env, jobject clazz, unsigned int generation; unsigned int numPorts; jint *nGeneration; - struct audio_port *nPorts = NULL; + struct audio_port_v7 *nPorts = nullptr; int attempts = MAX_PORT_GENERATION_SYNC_ATTEMPTS; jint jStatus; @@ -1448,11 +1424,8 @@ android_media_AudioSystem_listAudioPorts(JNIEnv *env, jobject clazz, } numPorts = 0; - status = AudioSystem::listAudioPorts(AUDIO_PORT_ROLE_NONE, - AUDIO_PORT_TYPE_NONE, - &numPorts, - NULL, - &generation1); + status = AudioSystem::listAudioPorts(AUDIO_PORT_ROLE_NONE, AUDIO_PORT_TYPE_NONE, &numPorts, + nullptr, &generation1); if (status != NO_ERROR) { ALOGE_IF(status != NO_ERROR, "AudioSystem::listAudioPorts error %d", status); break; @@ -1461,13 +1434,10 @@ android_media_AudioSystem_listAudioPorts(JNIEnv *env, jobject clazz, jStatus = (jint)AUDIO_JAVA_SUCCESS; goto exit; } - nPorts = (struct audio_port *)realloc(nPorts, numPorts * sizeof(struct audio_port)); + nPorts = (struct audio_port_v7 *)realloc(nPorts, numPorts * sizeof(struct audio_port_v7)); - status = AudioSystem::listAudioPorts(AUDIO_PORT_ROLE_NONE, - AUDIO_PORT_TYPE_NONE, - &numPorts, - nPorts, - &generation); + 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); @@ -2671,6 +2641,7 @@ int register_android_media_AudioSystem(JNIEnv *env) jclass arrayListClass = FindClassOrDie(env, "java/util/ArrayList"); gArrayListClass = MakeGlobalRefOrDie(env, arrayListClass); + gArrayListMethods.cstor = GetMethodIDOrDie(env, arrayListClass, "", "()V"); gArrayListMethods.add = GetMethodIDOrDie(env, arrayListClass, "add", "(Ljava/lang/Object;)Z"); gArrayListMethods.toArray = GetMethodIDOrDie(env, arrayListClass, "toArray", "()[Ljava/lang/Object;"); @@ -2733,7 +2704,7 @@ int register_android_media_AudioSystem(JNIEnv *env) gAudioDevicePortClass = MakeGlobalRefOrDie(env, audioDevicePortClass); gAudioDevicePortCstor = GetMethodIDOrDie(env, audioDevicePortClass, "", - "(Landroid/media/AudioHandle;Ljava/lang/String;[I[I[I[I" + "(Landroid/media/AudioHandle;Ljava/lang/String;Ljava/util/List;" "[Landroid/media/AudioGain;ILjava/lang/String;[I[I)V"); // When access AudioPort as AudioDevicePort @@ -2743,8 +2714,10 @@ int register_android_media_AudioSystem(JNIEnv *env) jclass audioMixPortClass = FindClassOrDie(env, "android/media/AudioMixPort"); gAudioMixPortClass = MakeGlobalRefOrDie(env, audioMixPortClass); - gAudioMixPortCstor = GetMethodIDOrDie(env, audioMixPortClass, "", - "(Landroid/media/AudioHandle;IILjava/lang/String;[I[I[I[I[Landroid/media/AudioGain;)V"); + gAudioMixPortCstor = + GetMethodIDOrDie(env, audioMixPortClass, "", + "(Landroid/media/AudioHandle;IILjava/lang/String;Ljava/util/List;" + "[Landroid/media/AudioGain;)V"); jclass audioGainClass = FindClassOrDie(env, "android/media/AudioGain"); gAudioGainClass = MakeGlobalRefOrDie(env, audioGainClass); @@ -2846,6 +2819,10 @@ int register_android_media_AudioSystem(JNIEnv *env) jclass listClass = FindClassOrDie(env, "java/util/List"); gListMethods.add = GetMethodIDOrDie(env, listClass, "add", "(Ljava/lang/Object;)Z"); + jclass audioProfileClass = FindClassOrDie(env, "android/media/AudioProfile"); + gAudioProfileClass = MakeGlobalRefOrDie(env, audioProfileClass); + gAudioProfileCstor = GetMethodIDOrDie(env, audioProfileClass, "", "(I[I[I[I)V"); + AudioSystem::addErrorCallback(android_media_AudioSystem_error_callback); RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods)); diff --git a/media/java/android/media/AudioDeviceInfo.java b/media/java/android/media/AudioDeviceInfo.java index b67851a6b64fc..2a3704df77db1 100644 --- a/media/java/android/media/AudioDeviceInfo.java +++ b/media/java/android/media/AudioDeviceInfo.java @@ -23,6 +23,7 @@ import android.util.SparseIntArray; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Arrays; +import java.util.List; import java.util.Objects; import java.util.TreeSet; @@ -497,6 +498,13 @@ public final class AudioDeviceInfo { return encodings; } + /** + * @return A list of {@link AudioProfile} supported by the audio devices. + */ + public @NonNull List getAudioProfiles() { + return mPort.profiles(); + } + /** * Returns an array of supported encapsulation modes for the device. * diff --git a/media/java/android/media/AudioDevicePort.java b/media/java/android/media/AudioDevicePort.java index 0f79675862c73..9c42bf7e56055 100644 --- a/media/java/android/media/AudioDevicePort.java +++ b/media/java/android/media/AudioDevicePort.java @@ -21,6 +21,7 @@ import android.compat.annotation.UnsupportedAppUsage; import android.os.Build; import java.util.Arrays; +import java.util.List; /** * The AudioDevicePort is a specialized type of AudioPort @@ -57,6 +58,18 @@ public class AudioDevicePort extends AudioPort { mEncapsulationMetadataTypes = encapsulationMetadataTypes; } + AudioDevicePort(AudioHandle handle, String deviceName, List profiles, + AudioGain[] gains, int type, String address, int[] encapsulationModes, + @AudioTrack.EncapsulationMetadataType int[] encapsulationMetadataTypes) { + super(handle, + AudioManager.isInputDevice(type) ? AudioPort.ROLE_SOURCE : AudioPort.ROLE_SINK, + deviceName, profiles, gains); + mType = type; + mAddress = address; + mEncapsulationModes = encapsulationModes; + mEncapsulationMetadataTypes = encapsulationMetadataTypes; + } + /** * Get the device type (e.g AudioManager.DEVICE_OUT_SPEAKER) */ diff --git a/media/java/android/media/AudioMixPort.java b/media/java/android/media/AudioMixPort.java index 0e286b0f1f895..7f2249d05ec25 100644 --- a/media/java/android/media/AudioMixPort.java +++ b/media/java/android/media/AudioMixPort.java @@ -19,6 +19,8 @@ package android.media; import android.compat.annotation.UnsupportedAppUsage; import android.os.Build; +import java.util.List; + /** * The AudioMixPort is a specialized type of AudioPort * describing an audio mix or stream at an input or output stream of the audio @@ -42,6 +44,12 @@ public class AudioMixPort extends AudioPort { mIoHandle = ioHandle; } + AudioMixPort(AudioHandle handle, int ioHandle, int role, String deviceName, + List profiles, AudioGain[] gains) { + super(handle, role, deviceName, profiles, gains); + mIoHandle = ioHandle; + } + /** * Build a specific configuration of this audio mix port for use by methods * like AudioManager.connectAudioPatch(). diff --git a/media/java/android/media/AudioPort.java b/media/java/android/media/AudioPort.java index e6dc622a11c18..03022501fbcae 100644 --- a/media/java/android/media/AudioPort.java +++ b/media/java/android/media/AudioPort.java @@ -19,6 +19,13 @@ package android.media; import android.compat.annotation.UnsupportedAppUsage; import android.os.Build; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + /** * An audio port is a node of the audio framework or hardware that can be connected to or * disconnect from another audio node to create a specific audio routing configuration. @@ -78,6 +85,7 @@ public class AudioPort { private final int[] mChannelMasks; private final int[] mChannelIndexMasks; private final int[] mFormats; + private final List mProfiles; @UnsupportedAppUsage private final AudioGain[] mGains; @UnsupportedAppUsage @@ -87,7 +95,6 @@ public class AudioPort { AudioPort(AudioHandle handle, int role, String name, int[] samplingRates, int[] channelMasks, int[] channelIndexMasks, int[] formats, AudioGain[] gains) { - mHandle = handle; mRole = role; mName = name; @@ -96,6 +103,39 @@ public class AudioPort { mChannelIndexMasks = channelIndexMasks; mFormats = formats; mGains = gains; + mProfiles = new ArrayList<>(); + if (mFormats != null) { + for (int format : mFormats) { + mProfiles.add(new AudioProfile( + format, samplingRates, channelMasks, channelIndexMasks)); + } + } + } + + AudioPort(AudioHandle handle, int role, String name, + List profiles, AudioGain[] gains) { + mHandle = handle; + mRole = role; + mName = name; + mProfiles = profiles; + mGains = gains; + Set formats = new HashSet<>(); + Set samplingRates = new HashSet<>(); + Set channelMasks = new HashSet<>(); + Set channelIndexMasks = new HashSet<>(); + for (AudioProfile profile : profiles) { + formats.add(profile.getFormat()); + samplingRates.addAll(Arrays.stream(profile.getSampleRates()).boxed() + .collect(Collectors.toList())); + channelMasks.addAll(Arrays.stream(profile.getChannelMasks()).boxed() + .collect(Collectors.toList())); + channelIndexMasks.addAll(Arrays.stream(profile.getChannelIndexMasks()).boxed() + .collect(Collectors.toList())); + } + mSamplingRates = samplingRates.stream().mapToInt(Number::intValue).toArray(); + mChannelMasks = channelMasks.stream().mapToInt(Number::intValue).toArray(); + mChannelIndexMasks = channelIndexMasks.stream().mapToInt(Number::intValue).toArray(); + mFormats = formats.stream().mapToInt(Number::intValue).toArray(); } AudioHandle handle() { @@ -162,6 +202,13 @@ public class AudioPort { return mFormats; } + /** + * Get the list of supported audio profiles + */ + public List profiles() { + return mProfiles; + } + /** * Get the list of gain descriptors * Empty array if this port does not have gain control diff --git a/media/java/android/media/AudioProfile.java b/media/java/android/media/AudioProfile.java new file mode 100644 index 0000000000000..42041667476a3 --- /dev/null +++ b/media/java/android/media/AudioProfile.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2020 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. + */ + +package android.media; + +import android.annotation.NonNull; + +/** + * AudioProfile is a class used by {@link AudioDeviceInfo} to list the audio formats, sampling rates + * and channel masks supported. + * Each AudioProfile is specific to an audio format and lists all sampling rates and channel masks + * supported for this format. + */ +public class AudioProfile { + private final int mFormat; + private final int[] mSamplingRates; + private final int[] mChannelMasks; + private final int[] mChannelIndexMasks; + + AudioProfile(int format, @NonNull int[] samplingRates, @NonNull int[] channelMasks, + @NonNull int[] channelIndexMasks) { + mFormat = format; + mSamplingRates = samplingRates; + mChannelMasks = channelMasks; + mChannelIndexMasks = channelIndexMasks; + } + + /** + * @return the encoding format for this AudioProfile. + */ + public @AudioFormat.Encoding int getFormat() { + return mFormat; + } + + /** + * @return an array of channel position masks that are associated with the encoding format. + */ + public @NonNull int[] getChannelMasks() { + return mChannelMasks; + } + + /** + * @return an array of channel index masks that are associated with the encoding format. + */ + public @NonNull int[] getChannelIndexMasks() { + return mChannelIndexMasks; + } + + /** + * @return an array of sample rates that are associated with the encoding format. + */ + public @NonNull int[] getSampleRates() { + return mSamplingRates; + } +}