blob: 404e27d5600fd389a30efaa7a090d75b7872a961 [file] [log] [blame]
/*
* Copyright (C) 2015 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.
*/
#pragma once
#include "policy.h"
#include <utils/String8.h>
#include <utils/SortedVector.h>
#include <utils/RefBase.h>
#include <utils/Errors.h>
#include <system/audio.h>
#include <cutils/config_utils.h>
namespace android {
typedef SortedVector<uint32_t> SampleRateVector;
typedef SortedVector<audio_channel_mask_t> ChannelsVector;
typedef Vector<audio_format_t> FormatVector;
template <typename T>
bool operator == (const SortedVector<T> &left, const SortedVector<T> &right);
class AudioProfile : public virtual RefBase
{
public:
AudioProfile(audio_format_t format,
audio_channel_mask_t channelMasks,
uint32_t samplingRate) :
mName(String8("")),
mFormat(format)
{
mChannelMasks.add(channelMasks);
mSamplingRates.add(samplingRate);
}
AudioProfile(audio_format_t format,
const ChannelsVector &channelMasks,
const SampleRateVector &samplingRateCollection) :
mName(String8("")),
mFormat(format),
mChannelMasks(channelMasks),
mSamplingRates(samplingRateCollection)
{}
audio_format_t getFormat() const { return mFormat; }
void setChannels(const ChannelsVector &channelMasks)
{
if (mIsDynamicChannels) {
mChannelMasks = channelMasks;
}
}
const ChannelsVector &getChannels() const { return mChannelMasks; }
void setSampleRates(const SampleRateVector &sampleRates)
{
if (mIsDynamicRate) {
mSamplingRates = sampleRates;
}
}
const SampleRateVector &getSampleRates() const { return mSamplingRates; }
bool isValid() const { return hasValidFormat() && hasValidRates() && hasValidChannels(); }
void clear()
{
if (mIsDynamicChannels) {
mChannelMasks.clear();
}
if (mIsDynamicRate) {
mSamplingRates.clear();
}
}
inline bool supportsChannels(audio_channel_mask_t channels) const
{
return mChannelMasks.indexOf(channels) >= 0;
}
inline bool supportsRate(uint32_t rate) const
{
return mSamplingRates.indexOf(rate) >= 0;
}
status_t checkExact(uint32_t rate, audio_channel_mask_t channels, audio_format_t format) const;
status_t checkCompatibleChannelMask(audio_channel_mask_t channelMask,
audio_channel_mask_t &updatedChannelMask,
audio_port_type_t portType,
audio_port_role_t portRole) const;
status_t checkCompatibleSamplingRate(uint32_t samplingRate,
uint32_t &updatedSamplingRate) const;
bool hasValidFormat() const { return mFormat != AUDIO_FORMAT_DEFAULT; }
bool hasValidRates() const { return !mSamplingRates.isEmpty(); }
bool hasValidChannels() const { return !mChannelMasks.isEmpty(); }
void setDynamicChannels(bool dynamic) { mIsDynamicChannels = dynamic; }
bool isDynamicChannels() const { return mIsDynamicChannels; }
void setDynamicRate(bool dynamic) { mIsDynamicRate = dynamic; }
bool isDynamicRate() const { return mIsDynamicRate; }
void setDynamicFormat(bool dynamic) { mIsDynamicFormat = dynamic; }
bool isDynamicFormat() const { return mIsDynamicFormat; }
bool isDynamic() { return mIsDynamicFormat || mIsDynamicChannels || mIsDynamicRate; }
void dump(int fd, int spaces) const;
private:
String8 mName;
audio_format_t mFormat;
ChannelsVector mChannelMasks;
SampleRateVector mSamplingRates;
bool mIsDynamicFormat = false;
bool mIsDynamicChannels = false;
bool mIsDynamicRate = false;
};
class AudioProfileVector : public Vector<sp<AudioProfile> >
{
public:
ssize_t add(const sp<AudioProfile> &profile)
{
ssize_t index = Vector::add(profile);
// we sort from worst to best, so that AUDIO_FORMAT_DEFAULT is always the first entry.
// TODO: compareFormats could be a lambda to convert between pointer-to-format to format:
// [](const audio_format_t *format1, const audio_format_t *format2) {
// return compareFormats(*format1, *format2);
// }
sort(compareFormats);
return index;
}
// This API is intended to be used by the policy manager once retrieving capabilities
// for a profile with dynamic format, rate and channels attributes
ssize_t addProfileFromHal(const sp<AudioProfile> &profileToAdd)
{
// Check valid profile to add:
if (!profileToAdd->hasValidFormat()) {
return -1;
}
if (!profileToAdd->hasValidChannels() && !profileToAdd->hasValidRates()) {
FormatVector formats;
formats.add(profileToAdd->getFormat());
setFormats(FormatVector(formats));
return 0;
}
if (!profileToAdd->hasValidChannels() && profileToAdd->hasValidRates()) {
setSampleRatesFor(profileToAdd->getSampleRates(), profileToAdd->getFormat());
return 0;
}
if (profileToAdd->hasValidChannels() && !profileToAdd->hasValidRates()) {
setChannelsFor(profileToAdd->getChannels(), profileToAdd->getFormat());
return 0;
}
// Go through the list of profile to avoid duplicates
for (size_t profileIndex = 0; profileIndex < size(); profileIndex++) {
const sp<AudioProfile> &profile = itemAt(profileIndex);
if (profile->isValid() && profile == profileToAdd) {
// Nothing to do
return profileIndex;
}
}
profileToAdd->setDynamicFormat(true); // set the format as dynamic to allow removal
return add(profileToAdd);
}
sp<AudioProfile> getFirstValidProfile() const
{
for (size_t i = 0; i < size(); i++) {
if (itemAt(i)->isValid()) {
return itemAt(i);
}
}
return 0;
}
bool hasValidProfile() const { return getFirstValidProfile() != 0; }
status_t checkExactProfile(uint32_t samplingRate, audio_channel_mask_t channelMask,
audio_format_t format) const;
status_t checkCompatibleProfile(uint32_t &samplingRate, audio_channel_mask_t &channelMask,
audio_format_t &format,
audio_port_type_t portType,
audio_port_role_t portRole) const;
FormatVector getSupportedFormats() const
{
FormatVector supportedFormats;
for (size_t i = 0; i < size(); i++) {
if (itemAt(i)->hasValidFormat()) {
supportedFormats.add(itemAt(i)->getFormat());
}
}
return supportedFormats;
}
bool hasDynamicProfile() const
{
for (size_t i = 0; i < size(); i++) {
if (itemAt(i)->isDynamic()) {
return true;
}
}
return false;
}
bool hasDynamicFormat() const
{
return getProfileFor(gDynamicFormat) != 0;
}
bool hasDynamicChannelsFor(audio_format_t format) const
{
for (size_t i = 0; i < size(); i++) {
sp<AudioProfile> profile = itemAt(i);
if (profile->getFormat() == format && profile->isDynamicChannels()) {
return true;
}
}
return false;
}
bool hasDynamicRateFor(audio_format_t format) const
{
for (size_t i = 0; i < size(); i++) {
sp<AudioProfile> profile = itemAt(i);
if (profile->getFormat() == format && profile->isDynamicRate()) {
return true;
}
}
return false;
}
// One audio profile will be added for each format supported by Audio HAL
void setFormats(const FormatVector &formats)
{
// Only allow to change the format of dynamic profile
sp<AudioProfile> dynamicFormatProfile = getProfileFor(gDynamicFormat);
if (dynamicFormatProfile == 0) {
return;
}
for (size_t i = 0; i < formats.size(); i++) {
sp<AudioProfile> profile = new AudioProfile(formats[i],
dynamicFormatProfile->getChannels(),
dynamicFormatProfile->getSampleRates());
profile->setDynamicFormat(true);
profile->setDynamicChannels(dynamicFormatProfile->isDynamicChannels());
profile->setDynamicRate(dynamicFormatProfile->isDynamicRate());
add(profile);
}
}
void clearProfiles()
{
for (size_t i = size(); i != 0; ) {
sp<AudioProfile> profile = itemAt(--i);
if (profile->isDynamicFormat() && profile->hasValidFormat()) {
removeAt(i);
continue;
}
profile->clear();
}
}
void dump(int fd, int spaces) const
{
const size_t SIZE = 256;
char buffer[SIZE];
snprintf(buffer, SIZE, "%*s- Profiles:\n", spaces, "");
write(fd, buffer, strlen(buffer));
for (size_t i = 0; i < size(); i++) {
snprintf(buffer, SIZE, "%*sProfile %zu:", spaces + 4, "", i);
write(fd, buffer, strlen(buffer));
itemAt(i)->dump(fd, spaces + 8);
}
}
private:
void setSampleRatesFor(const SampleRateVector &sampleRates, audio_format_t format)
{
for (size_t i = 0; i < size(); i++) {
sp<AudioProfile> profile = itemAt(i);
if (profile->getFormat() == format && profile->isDynamicRate()) {
if (profile->hasValidRates()) {
// Need to create a new profile with same format
sp<AudioProfile> profileToAdd = new AudioProfile(format, profile->getChannels(),
sampleRates);
profileToAdd->setDynamicFormat(true); // need to set to allow cleaning
add(profileToAdd);
} else {
profile->setSampleRates(sampleRates);
}
return;
}
}
}
void setChannelsFor(const ChannelsVector &channelMasks, audio_format_t format)
{
for (size_t i = 0; i < size(); i++) {
sp<AudioProfile> profile = itemAt(i);
if (profile->getFormat() == format && profile->isDynamicChannels()) {
if (profile->hasValidChannels()) {
// Need to create a new profile with same format
sp<AudioProfile> profileToAdd = new AudioProfile(format, channelMasks,
profile->getSampleRates());
profileToAdd->setDynamicFormat(true); // need to set to allow cleaning
add(profileToAdd);
} else {
profile->setChannels(channelMasks);
}
return;
}
}
}
sp<AudioProfile> getProfileFor(audio_format_t format) const
{
for (size_t i = 0; i < size(); i++) {
if (itemAt(i)->getFormat() == format) {
return itemAt(i);
}
}
return 0;
}
static int compareFormats(const sp<AudioProfile> *profile1, const sp<AudioProfile> *profile2);
};
bool operator == (const AudioProfile &left, const AudioProfile &right);
}; // namespace android