diff --git a/core/api/current.txt b/core/api/current.txt index 77d927b52b976..15902310c9cfd 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -24309,6 +24309,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(); @@ -24726,6 +24727,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; + } +}