blob: 1d3ad1cf3425476d9aea0a7f76a6da8452512abe [file] [log] [blame]
/*
* Copyright (C) 2018 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.
*/
#define LOG_TAG "APM::AudioPolicyEngine/ProductStrategy"
//#define LOG_NDEBUG 0
#include "ProductStrategy.h"
#include <media/AudioProductStrategy.h>
#include <media/TypeConverter.h>
#include <utils/String8.h>
#include <cstdint>
#include <string>
#include <log/log.h>
namespace android {
ProductStrategy::ProductStrategy(const std::string &name) :
mName(name),
mId(static_cast<product_strategy_t>(HandleGenerator<uint32_t>::getNextHandle()))
{
}
void ProductStrategy::addAttributes(const VolumeGroupAttributes &volumeGroupAttributes)
{
mAttributesVector.push_back(volumeGroupAttributes);
}
std::vector<android::VolumeGroupAttributes> ProductStrategy::listVolumeGroupAttributes() const
{
std::vector<android::VolumeGroupAttributes> androidAa;
for (const auto &attr : mAttributesVector) {
androidAa.push_back({attr.getGroupId(), attr.getStreamType(), attr.getAttributes()});
}
return androidAa;
}
AttributesVector ProductStrategy::getAudioAttributes() const
{
AttributesVector attrVector;
for (const auto &attrGroup : mAttributesVector) {
attrVector.push_back(attrGroup.getAttributes());
}
if (not attrVector.empty()) {
return attrVector;
}
return { AUDIO_ATTRIBUTES_INITIALIZER };
}
int ProductStrategy::matchesScore(const audio_attributes_t attr) const
{
int strategyScore = AudioProductStrategy::NO_MATCH;
for (const auto &attrGroup : mAttributesVector) {
int score = AudioProductStrategy::attributesMatchesScore(attrGroup.getAttributes(), attr);
if (score == AudioProductStrategy::MATCH_EQUALS) {
return score;
}
strategyScore = std::max(score, strategyScore);
}
return strategyScore;
}
audio_attributes_t ProductStrategy::getAttributesForStreamType(audio_stream_type_t streamType) const
{
const auto iter = std::find_if(begin(mAttributesVector), end(mAttributesVector),
[&streamType](const auto &supportedAttr) {
return supportedAttr.getStreamType() == streamType; });
return iter != end(mAttributesVector) ? iter->getAttributes() : AUDIO_ATTRIBUTES_INITIALIZER;
}
bool ProductStrategy::isDefault() const
{
return std::find_if(begin(mAttributesVector), end(mAttributesVector), [](const auto &attr) {
return attr.getAttributes() == defaultAttr; }) != end(mAttributesVector);
}
StreamTypeVector ProductStrategy::getSupportedStreams() const
{
StreamTypeVector streams;
for (const auto &supportedAttr : mAttributesVector) {
if (std::find(begin(streams), end(streams), supportedAttr.getStreamType())
== end(streams) && supportedAttr.getStreamType() != AUDIO_STREAM_DEFAULT) {
streams.push_back(supportedAttr.getStreamType());
}
}
return streams;
}
bool ProductStrategy::supportStreamType(const audio_stream_type_t &streamType) const
{
return std::find_if(begin(mAttributesVector), end(mAttributesVector),
[&streamType](const auto &supportedAttr) {
return supportedAttr.getStreamType() == streamType; }) != end(mAttributesVector);
}
volume_group_t ProductStrategy::getVolumeGroupForStreamType(audio_stream_type_t stream) const
{
for (const auto &supportedAttr : mAttributesVector) {
if (supportedAttr.getStreamType() == stream) {
return supportedAttr.getGroupId();
}
}
return VOLUME_GROUP_NONE;
}
volume_group_t ProductStrategy::getDefaultVolumeGroup() const
{
const auto &iter = std::find_if(begin(mAttributesVector), end(mAttributesVector),
[](const auto &attr) {
return attr.getAttributes() == defaultAttr;
});
return iter != end(mAttributesVector) ? iter->getGroupId() : VOLUME_GROUP_NONE;
}
void ProductStrategy::dump(String8 *dst, int spaces) const
{
dst->appendFormat("\n%*s-%s (id: %d)\n", spaces, "", mName.c_str(), mId);
std::string deviceLiteral = deviceTypesToString(mApplicableDevices);
dst->appendFormat("%*sSelected Device: {%s, @:%s}\n", spaces + 2, "",
deviceLiteral.c_str(), mDeviceAddress.c_str());
for (const auto &attr : mAttributesVector) {
dst->appendFormat("%*sGroup: %d stream: %s\n", spaces + 3, "", attr.getGroupId(),
android::toString(attr.getStreamType()).c_str());
dst->appendFormat("%*s Attributes: ", spaces + 3, "");
std::string attStr = attr.getAttributes() == defaultAttr ?
"{ Any }" : android::toString(attr.getAttributes());
dst->appendFormat("%s\n", attStr.c_str());
}
}
product_strategy_t ProductStrategyMap::getProductStrategyForAttributes(
const audio_attributes_t &attributes, bool fallbackOnDefault) const
{
product_strategy_t bestStrategyOrdefault = PRODUCT_STRATEGY_NONE;
int matchScore = AudioProductStrategy::NO_MATCH;
for (const auto &iter : *this) {
int score = iter.second->matchesScore(attributes);
if (score == AudioProductStrategy::MATCH_EQUALS) {
return iter.second->getId();
}
if (score > matchScore) {
bestStrategyOrdefault = iter.second->getId();;
matchScore = score;
}
}
return (matchScore != AudioProductStrategy::MATCH_ON_DEFAULT_SCORE || fallbackOnDefault) ?
bestStrategyOrdefault : PRODUCT_STRATEGY_NONE;
}
audio_attributes_t ProductStrategyMap::getAttributesForStreamType(audio_stream_type_t stream) const
{
for (const auto &iter : *this) {
const auto strategy = iter.second;
if (strategy->supportStreamType(stream)) {
return strategy->getAttributesForStreamType(stream);
}
}
ALOGV("%s: No product strategy for stream %s, using default", __FUNCTION__,
toString(stream).c_str());
return {};
}
product_strategy_t ProductStrategyMap::getDefault() const
{
if (mDefaultStrategy != PRODUCT_STRATEGY_NONE) {
return mDefaultStrategy;
}
for (const auto &iter : *this) {
if (iter.second->isDefault()) {
ALOGV("%s: using default %s", __FUNCTION__, iter.second->getName().c_str());
return iter.second->getId();
}
}
ALOGE("%s: No default product strategy defined", __FUNCTION__);
return PRODUCT_STRATEGY_NONE;
}
audio_attributes_t ProductStrategyMap::getAttributesForProductStrategy(
product_strategy_t strategy) const
{
if (find(strategy) == end()) {
ALOGE("Invalid %d strategy requested", strategy);
return AUDIO_ATTRIBUTES_INITIALIZER;
}
return at(strategy)->getAudioAttributes()[0];
}
product_strategy_t ProductStrategyMap::getProductStrategyForStream(audio_stream_type_t stream) const
{
for (const auto &iter : *this) {
if (iter.second->supportStreamType(stream)) {
return iter.second->getId();
}
}
ALOGV("%s: No product strategy for stream %d, using default", __FUNCTION__, stream);
return getDefault();
}
DeviceTypeSet ProductStrategyMap::getDeviceTypesForProductStrategy(
product_strategy_t strategy) const
{
if (find(strategy) == end()) {
ALOGE("Invalid %d strategy requested, returning device for default strategy", strategy);
product_strategy_t defaultStrategy = getDefault();
if (defaultStrategy == PRODUCT_STRATEGY_NONE) {
return {AUDIO_DEVICE_NONE};
}
return at(getDefault())->getDeviceTypes();
}
return at(strategy)->getDeviceTypes();
}
std::string ProductStrategyMap::getDeviceAddressForProductStrategy(product_strategy_t psId) const
{
if (find(psId) == end()) {
ALOGE("Invalid %d strategy requested, returning device for default strategy", psId);
product_strategy_t defaultStrategy = getDefault();
if (defaultStrategy == PRODUCT_STRATEGY_NONE) {
return {};
}
return at(getDefault())->getDeviceAddress();
}
return at(psId)->getDeviceAddress();
}
VolumeGroupAttributes ProductStrategyMap::getVolumeGroupAttributesForAttributes(
const audio_attributes_t &attr, bool fallbackOnDefault) const
{
int matchScore = AudioProductStrategy::NO_MATCH;
VolumeGroupAttributes bestVolumeGroupAttributes = {};
for (const auto &iter : *this) {
for (const auto &volGroupAttr : iter.second->getVolumeGroupAttributes()) {
int score = volGroupAttr.matchesScore(attr);
if (score == AudioProductStrategy::MATCH_EQUALS) {
return volGroupAttr;
}
if (score > matchScore) {
matchScore = score;
bestVolumeGroupAttributes = volGroupAttr;
}
}
}
return (matchScore != AudioProductStrategy::MATCH_ON_DEFAULT_SCORE || fallbackOnDefault) ?
bestVolumeGroupAttributes : VolumeGroupAttributes();
}
audio_stream_type_t ProductStrategyMap::getStreamTypeForAttributes(
const audio_attributes_t &attr) const
{
audio_stream_type_t streamType = getVolumeGroupAttributesForAttributes(
attr, /* fallbackOnDefault= */ true).getStreamType();
return streamType != AUDIO_STREAM_DEFAULT ? streamType : AUDIO_STREAM_MUSIC;
}
volume_group_t ProductStrategyMap::getVolumeGroupForAttributes(
const audio_attributes_t &attr, bool fallbackOnDefault) const
{
return getVolumeGroupAttributesForAttributes(attr, fallbackOnDefault).getGroupId();
}
volume_group_t ProductStrategyMap::getVolumeGroupForStreamType(
audio_stream_type_t stream, bool fallbackOnDefault) const
{
for (const auto &iter : *this) {
volume_group_t group = iter.second->getVolumeGroupForStreamType(stream);
if (group != VOLUME_GROUP_NONE) {
return group;
}
}
ALOGW("%s: no volume group for %s, using default", __func__, toString(stream).c_str());
return fallbackOnDefault ? getDefaultVolumeGroup() : VOLUME_GROUP_NONE;
}
volume_group_t ProductStrategyMap::getDefaultVolumeGroup() const
{
product_strategy_t defaultStrategy = getDefault();
if (defaultStrategy == PRODUCT_STRATEGY_NONE) {
return VOLUME_GROUP_NONE;
}
return at(defaultStrategy)->getDefaultVolumeGroup();
}
void ProductStrategyMap::initialize()
{
mDefaultStrategy = getDefault();
ALOG_ASSERT(mDefaultStrategy != PRODUCT_STRATEGY_NONE, "No default product strategy found");
}
void ProductStrategyMap::dump(String8 *dst, int spaces) const
{
dst->appendFormat("%*sProduct Strategies dump:", spaces, "");
for (const auto &iter : *this) {
iter.second->dump(dst, spaces + 2);
}
}
void dumpProductStrategyDevicesRoleMap(
const ProductStrategyDevicesRoleMap& productStrategyDeviceRoleMap,
String8 *dst,
int spaces) {
dst->appendFormat("\n%*sDevice role per product strategy dump:", spaces, "");
for (const auto& [strategyRolePair, devices] : productStrategyDeviceRoleMap) {
dst->appendFormat("\n%*sStrategy(%u) Device Role(%u) Devices(%s)", spaces + 2, "",
strategyRolePair.first, strategyRolePair.second,
dumpAudioDeviceTypeAddrVector(devices, true /*includeSensitiveInfo*/).c_str());
}
dst->appendFormat("\n");
}
}