blob: 525e9656d56598f28d9b6cbe73376d7a3dee5866 [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/Base"
//#define LOG_NDEBUG 0
#include "EngineBase.h"
#include "EngineDefaultConfig.h"
#include <TypeConverter.h>
namespace android {
namespace audio_policy {
void EngineBase::setObserver(AudioPolicyManagerObserver *observer)
{
ALOG_ASSERT(observer != NULL, "Invalid Audio Policy Manager observer");
mApmObserver = observer;
}
status_t EngineBase::initCheck()
{
return (mApmObserver != nullptr)? NO_ERROR : NO_INIT;
}
status_t EngineBase::setPhoneState(audio_mode_t state)
{
ALOGV("setPhoneState() state %d", state);
if (state < 0 || uint32_t(state) >= AUDIO_MODE_CNT) {
ALOGW("setPhoneState() invalid state %d", state);
return BAD_VALUE;
}
if (state == mPhoneState ) {
ALOGW("setPhoneState() setting same state %d", state);
return BAD_VALUE;
}
// store previous phone state for management of sonification strategy below
int oldState = mPhoneState;
mPhoneState = state;
if (!is_state_in_call(oldState) && is_state_in_call(state)) {
ALOGV(" Entering call in setPhoneState()");
switchVolumeCurve(AUDIO_STREAM_VOICE_CALL, AUDIO_STREAM_DTMF);
} else if (is_state_in_call(oldState) && !is_state_in_call(state)) {
ALOGV(" Exiting call in setPhoneState()");
restoreOriginVolumeCurve(AUDIO_STREAM_DTMF);
}
return NO_ERROR;
}
status_t EngineBase::setDeviceConnectionState(const sp<DeviceDescriptor> devDesc,
audio_policy_dev_state_t state)
{
audio_devices_t deviceType = devDesc->type();
if ((deviceType != AUDIO_DEVICE_NONE) && audio_is_output_device(deviceType)) {
mLastRemovableMediaDevices.setRemovableMediaDevices(devDesc, state);
}
return NO_ERROR;
}
product_strategy_t EngineBase::getProductStrategyForAttributes(const audio_attributes_t &attr) const
{
return mProductStrategies.getProductStrategyForAttributes(attr);
}
audio_stream_type_t EngineBase::getStreamTypeForAttributes(const audio_attributes_t &attr) const
{
return mProductStrategies.getStreamTypeForAttributes(attr);
}
audio_attributes_t EngineBase::getAttributesForStreamType(audio_stream_type_t stream) const
{
return mProductStrategies.getAttributesForStreamType(stream);
}
product_strategy_t EngineBase::getProductStrategyForStream(audio_stream_type_t stream) const
{
return mProductStrategies.getProductStrategyForStream(stream);
}
product_strategy_t EngineBase::getProductStrategyByName(const std::string &name) const
{
for (const auto &iter : mProductStrategies) {
if (iter.second->getName() == name) {
return iter.second->getId();
}
}
return PRODUCT_STRATEGY_NONE;
}
engineConfig::ParsingResult EngineBase::loadAudioPolicyEngineConfig()
{
auto loadVolumeConfig = [](auto &volumeGroups, auto &volumeConfig) {
sp<VolumeGroup> volumeGroup = new VolumeGroup(volumeConfig.name, volumeConfig.indexMin,
volumeConfig.indexMax);
volumeGroups[volumeGroup->getId()] = volumeGroup;
for (auto &configCurve : volumeConfig.volumeCurves) {
device_category deviceCat = DEVICE_CATEGORY_SPEAKER;
if (!DeviceCategoryConverter::fromString(configCurve.deviceCategory, deviceCat)) {
ALOGE("%s: Invalid %s", __FUNCTION__, configCurve.deviceCategory.c_str());
continue;
}
sp<VolumeCurve> curve = new VolumeCurve(deviceCat);
for (auto &point : configCurve.curvePoints) {
curve->add({point.index, point.attenuationInMb});
}
volumeGroup->add(curve);
}
return volumeGroup;
};
auto addSupportedStreamAttributes = [](auto &group, auto &volumeGroup, auto &strategy) {
volumeGroup->addSupportedStream(group.stream);
for (const auto &attr : group.attributesVect) {
strategy->addAttributes({group.stream, volumeGroup->getId(), attr});
volumeGroup->addSupportedAttributes(attr);
}
};
auto result = engineConfig::parse();
if (result.parsedConfig == nullptr) {
ALOGW("%s: No configuration found, using default matching phone experience.", __FUNCTION__);
engineConfig::Config config = gDefaultEngineConfig;
android::status_t ret = engineConfig::parseLegacyVolumes(config.volumeGroups);
result = {std::make_unique<engineConfig::Config>(config),
static_cast<size_t>(ret == NO_ERROR ? 0 : 1)};
}
// Append for internal use only strategies/volume groups (e.g. rerouting/patch)
result.parsedConfig->productStrategies.insert(
std::end(result.parsedConfig->productStrategies),
std::begin(gOrderedSystemStrategies), std::end(gOrderedSystemStrategies));
result.parsedConfig->volumeGroups.insert(
std::end(result.parsedConfig->volumeGroups),
std::begin(gSystemVolumeGroups), std::end(gSystemVolumeGroups));
ALOGE_IF(result.nbSkippedElement != 0, "skipped %zu elements", result.nbSkippedElement);
engineConfig::VolumeGroup defaultVolumeConfig;
for (auto &volumeConfig : result.parsedConfig->volumeGroups) {
// save default volume config for streams not defined in configuration
if (volumeConfig.name.compare("AUDIO_STREAM_MUSIC") == 0) {
defaultVolumeConfig = volumeConfig;
}
loadVolumeConfig(mVolumeGroups, volumeConfig);
}
for (auto& strategyConfig : result.parsedConfig->productStrategies) {
sp<ProductStrategy> strategy = new ProductStrategy(strategyConfig.name);
for (const auto &group : strategyConfig.attributesGroups) {
const auto &iter = std::find_if(begin(mVolumeGroups), end(mVolumeGroups),
[&group](const auto &volumeGroup) {
return group.volumeGroup == volumeGroup.second->getName(); });
if (group.stream != AUDIO_STREAM_DEFAULT) {
if (iter == end(mVolumeGroups)) {
ALOGW("%s: No configuration of %s found, using default volume configuration"
, __FUNCTION__, group.volumeGroup.c_str());
defaultVolumeConfig.name = group.volumeGroup;
sp<VolumeGroup> volumeGroup =
loadVolumeConfig(mVolumeGroups, defaultVolumeConfig);
addSupportedStreamAttributes(group, volumeGroup, strategy);
} else {
addSupportedStreamAttributes(group, iter->second, strategy);
}
}
}
product_strategy_t strategyId = strategy->getId();
mProductStrategies[strategyId] = strategy;
}
mProductStrategies.initialize();
return result;
}
StrategyVector EngineBase::getOrderedProductStrategies() const
{
auto findByFlag = [](const auto &productStrategies, auto flag) {
return std::find_if(begin(productStrategies), end(productStrategies),
[&](const auto &strategy) {
for (const auto &attributes : strategy.second->getAudioAttributes()) {
if ((attributes.flags & flag) == flag) {
return true;
}
}
return false;
});
};
auto strategies = mProductStrategies;
auto enforcedAudibleStrategyIter = findByFlag(strategies, AUDIO_FLAG_AUDIBILITY_ENFORCED);
if (getForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM) == AUDIO_POLICY_FORCE_SYSTEM_ENFORCED &&
enforcedAudibleStrategyIter != strategies.end()) {
auto enforcedAudibleStrategy = *enforcedAudibleStrategyIter;
strategies.erase(enforcedAudibleStrategyIter);
strategies.insert(begin(strategies), enforcedAudibleStrategy);
}
StrategyVector orderedStrategies;
for (const auto &iter : strategies) {
orderedStrategies.push_back(iter.second->getId());
}
return orderedStrategies;
}
StreamTypeVector EngineBase::getStreamTypesForProductStrategy(product_strategy_t ps) const
{
// @TODO default music stream to control volume if no group?
return (mProductStrategies.find(ps) != end(mProductStrategies)) ?
mProductStrategies.at(ps)->getSupportedStreams() :
StreamTypeVector(AUDIO_STREAM_MUSIC);
}
AttributesVector EngineBase::getAllAttributesForProductStrategy(product_strategy_t ps) const
{
return (mProductStrategies.find(ps) != end(mProductStrategies)) ?
mProductStrategies.at(ps)->getAudioAttributes() : AttributesVector();
}
status_t EngineBase::listAudioProductStrategies(AudioProductStrategyVector &strategies) const
{
for (const auto &iter : mProductStrategies) {
const auto &productStrategy = iter.second;
strategies.push_back(
{productStrategy->getName(), productStrategy->listAudioAttributes(),
productStrategy->getId()});
}
return NO_ERROR;
}
VolumeCurves *EngineBase::getVolumeCurvesForAttributes(const audio_attributes_t &attr) const
{
volume_group_t volGr = mProductStrategies.getVolumeGroupForAttributes(attr);
const auto &iter = mVolumeGroups.find(volGr);
LOG_ALWAYS_FATAL_IF(iter == std::end(mVolumeGroups), "No volume groups for %s", toString(attr).c_str());
return mVolumeGroups.at(volGr)->getVolumeCurves();
}
VolumeCurves *EngineBase::getVolumeCurvesForStreamType(audio_stream_type_t stream) const
{
volume_group_t volGr = mProductStrategies.getVolumeGroupForStreamType(stream);
if (volGr == VOLUME_GROUP_NONE) {
volGr = mProductStrategies.getDefaultVolumeGroup();
}
const auto &iter = mVolumeGroups.find(volGr);
LOG_ALWAYS_FATAL_IF(iter == std::end(mVolumeGroups), "No volume groups for %s",
toString(stream).c_str());
return mVolumeGroups.at(volGr)->getVolumeCurves();
}
status_t EngineBase::switchVolumeCurve(audio_stream_type_t streamSrc, audio_stream_type_t streamDst)
{
auto srcCurves = getVolumeCurvesForStreamType(streamSrc);
auto dstCurves = getVolumeCurvesForStreamType(streamDst);
if (srcCurves == nullptr || dstCurves == nullptr) {
return BAD_VALUE;
}
return dstCurves->switchCurvesFrom(*srcCurves);
}
status_t EngineBase::restoreOriginVolumeCurve(audio_stream_type_t stream)
{
VolumeCurves *curves = getVolumeCurvesForStreamType(stream);
return curves != nullptr ? curves->switchCurvesFrom(*curves) : BAD_VALUE;
}
VolumeGroupVector EngineBase::getVolumeGroups() const
{
VolumeGroupVector group;
for (const auto &iter : mVolumeGroups) {
group.push_back(iter.first);
}
return group;
}
volume_group_t EngineBase::getVolumeGroupForAttributes(const audio_attributes_t &attr) const
{
return mProductStrategies.getVolumeGroupForAttributes(attr);
}
volume_group_t EngineBase::getVolumeGroupForStreamType(audio_stream_type_t stream) const
{
return mProductStrategies.getVolumeGroupForStreamType(stream);
}
status_t EngineBase::listAudioVolumeGroups(AudioVolumeGroupVector &groups) const
{
for (const auto &iter : mVolumeGroups) {
groups.push_back({iter.second->getName(), iter.second->getId(),
iter.second->getSupportedAttributes(), iter.second->getStreamTypes()});
}
return NO_ERROR;
}
status_t EngineBase::setPreferredDeviceForStrategy(product_strategy_t strategy,
const AudioDeviceTypeAddr &device)
{
// verify strategy exists
if (mProductStrategies.find(strategy) == mProductStrategies.end()) {
ALOGE("%s invalid strategy %u", __func__, strategy);
return BAD_VALUE;
}
mProductStrategyPreferredDevices[strategy] = device;
return NO_ERROR;
}
status_t EngineBase::removePreferredDeviceForStrategy(product_strategy_t strategy)
{
// verify strategy exists
if (mProductStrategies.find(strategy) == mProductStrategies.end()) {
ALOGE("%s invalid strategy %u", __func__, strategy);
return BAD_VALUE;
}
if (mProductStrategyPreferredDevices.erase(strategy) == 0) {
// no preferred device was set
return NAME_NOT_FOUND;
}
return NO_ERROR;
}
status_t EngineBase::getPreferredDeviceForStrategy(product_strategy_t strategy,
AudioDeviceTypeAddr &device) const
{
// verify strategy exists
if (mProductStrategies.find(strategy) == mProductStrategies.end()) {
ALOGE("%s unknown strategy %u", __func__, strategy);
return BAD_VALUE;
}
// preferred device for this strategy?
auto devIt = mProductStrategyPreferredDevices.find(strategy);
if (devIt == mProductStrategyPreferredDevices.end()) {
ALOGV("%s no preferred device for strategy %u", __func__, strategy);
return NAME_NOT_FOUND;
}
device = devIt->second;
return NO_ERROR;
}
void EngineBase::dump(String8 *dst) const
{
mProductStrategies.dump(dst, 2);
mProductStrategyPreferredDevices.dump(dst, 2);
mVolumeGroups.dump(dst, 2);
}
} // namespace audio_policy
} // namespace android