audiopolicy: engine: Add ProductStrategy common code
-Adds new Engine APIs for Product Strategy management
-Adds a common engine code to handle product strategies
-Adds a parsing configuration library to feed the strategies
-Prepare both engine for the switch
Test: make
Change-Id: I00f57ece798893bc6f9aa9ed54a3e5237e8d5cf1
diff --git a/services/audiopolicy/engine/Android.mk b/services/audiopolicy/engine/Android.mk
new file mode 100644
index 0000000..dcce8e3
--- /dev/null
+++ b/services/audiopolicy/engine/Android.mk
@@ -0,0 +1,9 @@
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+#######################################################################
+# Recursive call sub-folder Android.mk
+#
+include $(call all-makefiles-under,$(LOCAL_PATH))
+
diff --git a/services/audiopolicy/engine/common/Android.bp b/services/audiopolicy/engine/common/Android.bp
new file mode 100644
index 0000000..e6ede07
--- /dev/null
+++ b/services/audiopolicy/engine/common/Android.bp
@@ -0,0 +1,19 @@
+// 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.
+
+cc_library_headers {
+ name: "libaudiopolicyengine_common_headers",
+ host_supported: true,
+ export_include_dirs: ["include"],
+}
diff --git a/services/audiopolicy/engine/common/include/EngineBase.h b/services/audiopolicy/engine/common/include/EngineBase.h
new file mode 100644
index 0000000..32898b1
--- /dev/null
+++ b/services/audiopolicy/engine/common/include/EngineBase.h
@@ -0,0 +1,99 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <EngineConfig.h>
+#include <AudioPolicyManagerInterface.h>
+#include <ProductStrategy.h>
+
+namespace android {
+namespace audio_policy {
+
+class EngineBase : public AudioPolicyManagerInterface
+{
+public:
+ ///
+ /// from AudioPolicyManagerInterface
+ ///
+ android::status_t initCheck() override;
+
+ void setObserver(AudioPolicyManagerObserver *observer) override;
+
+ status_t setPhoneState(audio_mode_t mode) override;
+
+ audio_mode_t getPhoneState() const override { return mPhoneState; }
+
+ status_t setForceUse(audio_policy_force_use_t usage, audio_policy_forced_cfg_t config) override
+ {
+ mForceUse[usage] = config;
+ return NO_ERROR;
+ }
+
+ audio_policy_forced_cfg_t getForceUse(audio_policy_force_use_t usage) const override
+ {
+ return mForceUse[usage];
+ }
+ android::status_t setDeviceConnectionState(const sp<DeviceDescriptor> /*devDesc*/,
+ audio_policy_dev_state_t /*state*/) override
+ {
+ return NO_ERROR;
+ }
+ product_strategy_t getProductStrategyForAttributes(
+ const audio_attributes_t &attr) const override;
+
+ audio_stream_type_t getStreamTypeForAttributes(const audio_attributes_t &attr) const override;
+
+ audio_attributes_t getAttributesForStreamType(audio_stream_type_t stream) const override;
+
+ StreamTypeVector getStreamTypesForProductStrategy(product_strategy_t ps) const override;
+
+ AttributesVector getAllAttributesForProductStrategy(product_strategy_t ps) const override;
+
+ StrategyVector getOrderedProductStrategies() const override;
+
+ void dump(String8 *dst) const override;
+
+
+ engineConfig::ParsingResult loadAudioPolicyEngineConfig();
+
+ const ProductStrategyMap &getProductStrategies() const { return mProductStrategies; }
+
+ ProductStrategyMap &getProductStrategies() { return mProductStrategies; }
+
+ product_strategy_t getProductStrategyForStream(audio_stream_type_t stream) const;
+
+ product_strategy_t getProductStrategyByName(const std::string &name) const;
+
+ AudioPolicyManagerObserver *getApmObserver() const { return mApmObserver; }
+
+ inline bool isInCall() const
+ {
+ return is_state_in_call(getPhoneState());
+ }
+
+private:
+ AudioPolicyManagerObserver *mApmObserver = nullptr;
+
+ ProductStrategyMap mProductStrategies;
+ audio_mode_t mPhoneState = AUDIO_MODE_NORMAL; /**< current phone state. */
+
+ /** current forced use configuration. */
+ audio_policy_forced_cfg_t mForceUse[AUDIO_POLICY_FORCE_USE_CNT] = {};
+};
+
+} // namespace audio_policy
+} // namespace android
diff --git a/services/audiopolicy/engine/common/include/ProductStrategy.h b/services/audiopolicy/engine/common/include/ProductStrategy.h
new file mode 100644
index 0000000..66ae86e
--- /dev/null
+++ b/services/audiopolicy/engine/common/include/ProductStrategy.h
@@ -0,0 +1,156 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <system/audio.h>
+#include <AudioPolicyManagerInterface.h>
+#include <utils/RefBase.h>
+#include <HandleGenerator.h>
+#include <string>
+#include <vector>
+#include <map>
+#include <utils/Errors.h>
+#include <utils/String8.h>
+
+namespace android {
+
+/**
+ * @brief The ProductStrategy class describes for each product_strategy_t identifier the
+ * associated audio attributes, the device types to use, the device address to use.
+ * The identifier is voluntarily not strongly typed in order to be extensible by OEM.
+ */
+class ProductStrategy : public virtual RefBase, private HandleGenerator<uint32_t>
+{
+private:
+ struct AudioAttributes {
+ audio_stream_type_t mStream = AUDIO_STREAM_DEFAULT;
+ uint32_t mGroupId = 0;
+ audio_attributes_t mAttributes = AUDIO_ATTRIBUTES_INITIALIZER;
+ };
+
+ using AudioAttributesVector = std::vector<AudioAttributes>;
+
+public:
+ ProductStrategy(const std::string &name);
+
+ void addAttributes(const AudioAttributes &audioAttributes);
+
+ std::string getName() const { return mName; }
+ AttributesVector getAudioAttributes() const;
+ product_strategy_t getId() const { return mId; }
+ StreamTypeVector getSupportedStreams() const;
+
+ /**
+ * @brief matches checks if the given audio attributes shall follow the strategy.
+ * Order of the attributes within a strategy matters.
+ * If only the usage is available, the check is performed on the usages of the given
+ * attributes, otherwise all fields must match.
+ * @param attributes to consider
+ * @return true if attributes matches with the strategy, false otherwise.
+ */
+ bool matches(const audio_attributes_t attributes) const;
+
+ bool supportStreamType(const audio_stream_type_t &streamType) const;
+
+ void setDeviceAddress(const std::string &address)
+ {
+ mDeviceAddress = address;
+ }
+
+ std::string getDeviceAddress() const { return mDeviceAddress; }
+
+ void setDeviceTypes(audio_devices_t devices)
+ {
+ mApplicableDevices = devices;
+ }
+
+ audio_devices_t getDeviceTypes() const { return mApplicableDevices; }
+
+ audio_attributes_t getAttributesForStreamType(audio_stream_type_t stream) const;
+ audio_stream_type_t getStreamTypeForAttributes(const audio_attributes_t &attr) const;
+
+ bool isDefault() const;
+
+ void dump(String8 *dst, int spaces = 0) const;
+
+ /**
+ * @brief attributesMatches: checks if client attributes matches with a reference attributes
+ * "matching" means the usage shall match if reference attributes has a defined usage, AND
+ * content type shall match if reference attributes has a defined content type AND
+ * flags shall match if reference attributes has defined flags AND
+ * tags shall match if reference attributes has defined tags.
+ * Reference attributes "default" shall not be considered as a "true" case. This convention
+ * is used to identify the default strategy.
+ * @param refAttributes to be considered
+ * @param clientAttritubes to be considered
+ * @return true if matching, false otherwise
+ */
+ static bool attributesMatches(const audio_attributes_t refAttributes,
+ const audio_attributes_t clientAttritubes);
+private:
+ std::string mName;
+
+ AudioAttributesVector mAttributesVector;
+
+ product_strategy_t mId;
+
+ std::string mDeviceAddress; /**< Device address applicable for this strategy, maybe empty */
+
+ /**
+ * Applicable device(s) type mask for this strategy.
+ */
+ audio_devices_t mApplicableDevices = AUDIO_DEVICE_NONE;
+};
+
+class ProductStrategyMap : public std::map<product_strategy_t, sp<ProductStrategy> >
+{
+public:
+ /**
+ * @brief getProductStrategyForAttribute. The order of the vector is dimensionning.
+ * @param attr
+ * @return applicable product strategy for the given attribute, default if none applicable.
+ */
+ product_strategy_t getProductStrategyForAttributes(const audio_attributes_t &attr) const;
+
+ product_strategy_t getProductStrategyForStream(audio_stream_type_t stream) const;
+
+ audio_attributes_t getAttributesForStreamType(audio_stream_type_t stream) const;
+
+ audio_stream_type_t getStreamTypeForAttributes(const audio_attributes_t &attr) const;
+
+ /**
+ * @brief getAttributesForProductStrategy can be called from
+ * AudioManager: in this case, the product strategy IS the former routing strategy
+ * CarAudioManager: in this case, the product strategy IS the car usage
+ * [getAudioAttributesForCarUsage]
+ * OemExtension: in this case, the product strategy IS the Oem usage
+ *
+ * @param strategy
+ * @return audio attributes (or at least one of the attributes) following the given strategy.
+ */
+ audio_attributes_t getAttributesForProductStrategy(product_strategy_t strategy) const;
+
+ audio_devices_t getDeviceTypesForProductStrategy(product_strategy_t strategy) const;
+
+ std::string getDeviceAddressForProductStrategy(product_strategy_t strategy) const;
+
+ product_strategy_t getDefault() const;
+
+ void dump(String8 *dst, int spaces = 0) const;
+};
+
+} // namespace android
diff --git a/services/audiopolicy/engine/common/src/EngineBase.cpp b/services/audiopolicy/engine/common/src/EngineBase.cpp
new file mode 100644
index 0000000..0f4d5a5
--- /dev/null
+++ b/services/audiopolicy/engine/common/src/EngineBase.cpp
@@ -0,0 +1,171 @@
+/*
+ * 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 || 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()");
+ } else if (is_state_in_call(oldState) && !is_state_in_call(state)) {
+ ALOGV(" Exiting call in setPhoneState()");
+ }
+ 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 loadProductStrategies =
+ [](auto& strategyConfigs, auto& productStrategies) {
+ uint32_t groupid = 0;
+ for (auto& strategyConfig : strategyConfigs) {
+ sp<ProductStrategy> strategy = new ProductStrategy(strategyConfig.name);
+ for (const auto &group : strategyConfig.attributesGroups) {
+ for (const auto &attr : group.attributesVect) {
+ strategy->addAttributes({group.stream, groupid, attr});
+ }
+ groupid += 1;
+ }
+ product_strategy_t strategyId = strategy->getId();
+ productStrategies[strategyId] = strategy;
+ }
+ };
+
+ auto result = engineConfig::parse();
+ if (result.parsedConfig == nullptr) {
+ ALOGW("%s: No configuration found, using default matching phone experience.", __FUNCTION__);
+ result = {std::make_unique<engineConfig::Config>(gDefaultEngineConfig), 0};
+ }
+ ALOGE_IF(result.nbSkippedElement != 0, "skipped %zu elements", result.nbSkippedElement);
+ loadProductStrategies(result.parsedConfig->productStrategies, mProductStrategies);
+ 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();
+}
+
+void EngineBase::dump(String8 *dst) const
+{
+ mProductStrategies.dump(dst, 2);
+}
+
+} // namespace audio_policy
+} // namespace android
diff --git a/services/audiopolicy/engine/common/src/EngineDefaultConfig.h b/services/audiopolicy/engine/common/src/EngineDefaultConfig.h
new file mode 100644
index 0000000..5f546b3
--- /dev/null
+++ b/services/audiopolicy/engine/common/src/EngineDefaultConfig.h
@@ -0,0 +1,138 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <system/audio.h>
+
+namespace android {
+/**
+ * @brief AudioProductStrategies hard coded array of strategies to fill new engine API contract.
+ */
+const engineConfig::ProductStrategies gOrderedStrategies = {
+ {"STRATEGY_PHONE",
+ {
+ {"phone", AUDIO_STREAM_VOICE_CALL,
+ {{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_VOICE_COMMUNICATION, AUDIO_SOURCE_DEFAULT, 0,
+ ""}},
+ },
+ {"sco", AUDIO_STREAM_BLUETOOTH_SCO,
+ {{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_UNKNOWN, AUDIO_SOURCE_DEFAULT, AUDIO_FLAG_SCO,
+ ""}},
+ }
+ },
+ },
+ {"STRATEGY_SONIFICATION",
+ {
+ {"ring", AUDIO_STREAM_RING,
+ {{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE,
+ AUDIO_SOURCE_DEFAULT, 0, ""}}
+ },
+ {"alarm", AUDIO_STREAM_ALARM,
+ {{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_ALARM, AUDIO_SOURCE_DEFAULT, 0, ""}},
+ }
+ },
+ },
+ {"STRATEGY_ENFORCED_AUDIBLE",
+ {
+ {"", AUDIO_STREAM_ENFORCED_AUDIBLE,
+ {{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_UNKNOWN, AUDIO_SOURCE_DEFAULT,
+ AUDIO_FLAG_AUDIBILITY_ENFORCED, ""}}
+ }
+ },
+ },
+ {"STRATEGY_ACCESSIBILITY",
+ {
+ {"", AUDIO_STREAM_ACCESSIBILITY,
+ {{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY,
+ AUDIO_SOURCE_DEFAULT, 0, ""}}
+ }
+ },
+ },
+ {"STRATEGY_SONIFICATION_RESPECTFUL",
+ {
+ {"", AUDIO_STREAM_NOTIFICATION,
+ {
+ {AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_NOTIFICATION, AUDIO_SOURCE_DEFAULT, 0, ""},
+ {AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_NOTIFICATION_COMMUNICATION_REQUEST,
+ AUDIO_SOURCE_DEFAULT, 0, ""},
+ {AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_NOTIFICATION_COMMUNICATION_INSTANT,
+ AUDIO_SOURCE_DEFAULT, 0, ""},
+ {AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_NOTIFICATION_COMMUNICATION_DELAYED,
+ AUDIO_SOURCE_DEFAULT, 0, ""},
+ {AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_NOTIFICATION_EVENT,
+ AUDIO_SOURCE_DEFAULT, 0, ""}
+ }
+ }
+ },
+ },
+ {"STRATEGY_MEDIA",
+ {
+ {"music", AUDIO_STREAM_MUSIC,
+ {
+ {AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_MEDIA, AUDIO_SOURCE_DEFAULT, 0, ""},
+ {AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_GAME, AUDIO_SOURCE_DEFAULT, 0, ""},
+ {AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_ASSISTANT, AUDIO_SOURCE_DEFAULT, 0, ""},
+ {AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE,
+ AUDIO_SOURCE_DEFAULT, 0, ""},
+ {AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_UNKNOWN, AUDIO_SOURCE_DEFAULT, 0, ""}
+ },
+ },
+ {"system", AUDIO_STREAM_SYSTEM,
+ {{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_ASSISTANCE_SONIFICATION,
+ AUDIO_SOURCE_DEFAULT, 0, ""}}
+ }
+ },
+ },
+ {"STRATEGY_DTMF",
+ {
+ {"", AUDIO_STREAM_DTMF,
+ {
+ {AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING,
+ AUDIO_SOURCE_DEFAULT, 0, ""}
+ }
+ }
+ },
+ },
+ {"STRATEGY_TRANSMITTED_THROUGH_SPEAKER",
+ {
+ {"", AUDIO_STREAM_TTS,
+ {{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_UNKNOWN, AUDIO_SOURCE_DEFAULT,
+ AUDIO_FLAG_BEACON, ""}}
+ }
+ },
+ },
+ {"STRATEGY_REROUTING",
+ {
+ {"", AUDIO_STREAM_REROUTING,
+ {{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_UNKNOWN, AUDIO_SOURCE_DEFAULT, 0, ""}}
+ }
+ },
+ },
+ {"STRATEGY_PATCH",
+ {
+ {"", AUDIO_STREAM_PATCH,
+ {{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_UNKNOWN, AUDIO_SOURCE_DEFAULT, 0, ""}}
+ }
+ },
+ }
+};
+
+const engineConfig::Config gDefaultEngineConfig = {
+ 1.0,
+ gOrderedStrategies
+};
+} // namespace android
diff --git a/services/audiopolicy/engine/common/src/ProductStrategy.cpp b/services/audiopolicy/engine/common/src/ProductStrategy.cpp
new file mode 100644
index 0000000..a3edb39
--- /dev/null
+++ b/services/audiopolicy/engine/common/src/ProductStrategy.cpp
@@ -0,0 +1,257 @@
+/*
+ * 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/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 AudioAttributes &audioAttributes)
+{
+ mAttributesVector.push_back(audioAttributes);
+}
+
+AttributesVector ProductStrategy::getAudioAttributes() const
+{
+ AttributesVector attrVector;
+ for (const auto &attrGroup : mAttributesVector) {
+ attrVector.push_back(attrGroup.mAttributes);
+ }
+ if (not attrVector.empty()) {
+ return attrVector;
+ }
+ return { AUDIO_ATTRIBUTES_INITIALIZER };
+}
+
+// @todo: all flags required to match?
+// all tags required to match?
+/* static */
+bool ProductStrategy::attributesMatches(const audio_attributes_t refAttributes,
+ const audio_attributes_t clientAttritubes)
+{
+ if (refAttributes == defaultAttr) {
+ // The default product strategy is the strategy that holds default attributes by convention.
+ // All attributes that fail to match will follow the default strategy for routing.
+ // Choosing the default must be done as a fallback, the attributes match shall not
+ // selects the default.
+ return false;
+ }
+ return ((refAttributes.usage == AUDIO_USAGE_UNKNOWN) ||
+ (clientAttritubes.usage == refAttributes.usage)) &&
+ ((refAttributes.content_type == AUDIO_CONTENT_TYPE_UNKNOWN) ||
+ (clientAttritubes.content_type == refAttributes.content_type)) &&
+ ((refAttributes.flags == AUDIO_OUTPUT_FLAG_NONE) ||
+ (clientAttritubes.flags != AUDIO_OUTPUT_FLAG_NONE &&
+ (clientAttritubes.flags & refAttributes.flags) == clientAttritubes.flags)) &&
+ ((strlen(refAttributes.tags) == 0) ||
+ (std::strcmp(clientAttritubes.tags, refAttributes.tags) == 0));
+}
+
+bool ProductStrategy::matches(const audio_attributes_t attr) const
+{
+ return std::find_if(begin(mAttributesVector), end(mAttributesVector),
+ [&attr](const auto &supportedAttr) {
+ return attributesMatches(supportedAttr.mAttributes, attr); }) != end(mAttributesVector);
+}
+
+audio_stream_type_t ProductStrategy::getStreamTypeForAttributes(const audio_attributes_t &attr) const
+{
+ const auto iter = std::find_if(begin(mAttributesVector), end(mAttributesVector),
+ [&attr](const auto &supportedAttr) {
+ return attributesMatches(supportedAttr.mAttributes, attr); });
+ return iter != end(mAttributesVector) ? iter->mStream : AUDIO_STREAM_DEFAULT;
+}
+
+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.mStream == streamType; });
+ return iter != end(mAttributesVector) ? iter->mAttributes : AUDIO_ATTRIBUTES_INITIALIZER;
+}
+
+bool ProductStrategy::isDefault() const
+{
+ return std::find_if(begin(mAttributesVector), end(mAttributesVector), [](const auto &attr) {
+ return attr.mAttributes == defaultAttr; }) != end(mAttributesVector);
+}
+
+StreamTypeVector ProductStrategy::getSupportedStreams() const
+{
+ StreamTypeVector streams;
+ for (const auto &supportedAttr : mAttributesVector) {
+ if (std::find(begin(streams), end(streams), supportedAttr.mStream) == end(streams) &&
+ supportedAttr.mStream != AUDIO_STREAM_DEFAULT) {
+ streams.push_back(supportedAttr.mStream);
+ }
+ }
+ 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.mStream == streamType; }) != end(mAttributesVector);
+}
+
+void ProductStrategy::dump(String8 *dst, int spaces) const
+{
+ dst->appendFormat("\n%*s-%s (id: %d)\n", spaces, "", mName.c_str(), mId);
+ std::string deviceLiteral;
+ if (!OutputDeviceConverter::toString(mApplicableDevices, deviceLiteral)) {
+ ALOGE("%s: failed to convert device %d", __FUNCTION__, mApplicableDevices);
+ }
+ dst->appendFormat("%*sSelected Device: {type:%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.mGroupId,
+ android::toString(attr.mStream).c_str());
+ dst->appendFormat("%*s Attributes: ", spaces + 3, "");
+ std::string attStr =
+ attr.mAttributes == defaultAttr ? "{ Any }" : android::toString(attr.mAttributes);
+ dst->appendFormat("%s\n", attStr.c_str());
+ }
+}
+
+product_strategy_t ProductStrategyMap::getProductStrategyForAttributes(
+ const audio_attributes_t &attr) const
+{
+ for (const auto &iter : *this) {
+ if (iter.second->matches(attr)) {
+ return iter.second->getId();
+ }
+ }
+ ALOGV("%s: No matching product strategy for attributes %s, return default", __FUNCTION__,
+ toString(attr).c_str());
+ return getDefault();
+}
+
+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 {};
+}
+
+audio_stream_type_t ProductStrategyMap::getStreamTypeForAttributes(
+ const audio_attributes_t &attr) const
+{
+ for (const auto &iter : *this) {
+ audio_stream_type_t stream = iter.second->getStreamTypeForAttributes(attr);
+ if (stream != AUDIO_STREAM_DEFAULT) {
+ return stream;
+ }
+ }
+ ALOGV("%s: No product strategy for attributes %s, using default (aka MUSIC)", __FUNCTION__,
+ toString(attr).c_str());
+ return AUDIO_STREAM_MUSIC;
+}
+
+product_strategy_t ProductStrategyMap::getDefault() const
+{
+ 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();
+}
+
+
+audio_devices_t 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();
+}
+
+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);
+ }
+}
+
+}
+
diff --git a/services/audiopolicy/engine/config/Android.mk b/services/audiopolicy/engine/config/Android.mk
new file mode 100644
index 0000000..dbcd1bf
--- /dev/null
+++ b/services/audiopolicy/engine/config/Android.mk
@@ -0,0 +1,41 @@
+LOCAL_PATH := $(call my-dir)
+
+##################################################################
+# Component build
+##################################################################
+
+include $(CLEAR_VARS)
+
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+
+LOCAL_C_INCLUDES := \
+ $(LOCAL_EXPORT_C_INCLUDE_DIRS) \
+ external/libxml2/include \
+ external/icu/icu4c/source/common
+
+LOCAL_SRC_FILES := \
+ src/EngineConfig.cpp
+
+LOCAL_CFLAGS += -Wall -Werror -Wextra
+
+LOCAL_SHARED_LIBRARIES := \
+ libparameter \
+ libmedia_helper \
+ libandroidicu \
+ libxml2 \
+ libutils \
+ liblog
+
+LOCAL_STATIC_LIBRARIES := \
+ libaudiopolicycomponents
+
+LOCAL_MULTILIB := $(AUDIOSERVER_MULTILIB)
+
+LOCAL_MODULE := libaudiopolicyengineconfig
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_HEADER_LIBRARIES := \
+ libaudio_system_headers \
+ libaudiopolicycommon
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/services/audiopolicy/engine/config/include/EngineConfig.h b/services/audiopolicy/engine/config/include/EngineConfig.h
new file mode 100644
index 0000000..64b9526
--- /dev/null
+++ b/services/audiopolicy/engine/config/include/EngineConfig.h
@@ -0,0 +1,73 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <system/audio.h>
+
+#include <string>
+#include <vector>
+#include <utils/Errors.h>
+
+struct _xmlNode;
+struct _xmlDoc;
+
+namespace android {
+namespace engineConfig {
+
+/** Default path of audio policy usages configuration file. */
+constexpr char DEFAULT_PATH[] = "/vendor/etc/audio_policy_engine_configuration.xml";
+
+/** Directories where the effect libraries will be search for. */
+constexpr const char* POLICY_USAGE_LIBRARY_PATH[] = {"/odm/etc/", "/vendor/etc/", "/system/etc/"};
+
+using AttributesVector = std::vector<audio_attributes_t>;
+using StreamVector = std::vector<audio_stream_type_t>;
+
+struct AttributesGroup {
+ std::string name;
+ audio_stream_type_t stream;
+ AttributesVector attributesVect;
+};
+
+using AttributesGroups = std::vector<AttributesGroup>;
+
+struct ProductStrategy {
+ std::string name;
+ AttributesGroups attributesGroups;
+};
+
+using ProductStrategies = std::vector<ProductStrategy>;
+
+struct Config {
+ float version;
+ ProductStrategies productStrategies;
+};
+
+/** Result of `parse(const char*)` */
+struct ParsingResult {
+ /** Parsed config, nullptr if the xml lib could not load the file */
+ std::unique_ptr<Config> parsedConfig;
+ size_t nbSkippedElement; //< Number of skipped invalid product strategies
+};
+
+/** Parses the provided audio policy usage configuration.
+ * @return audio policy usage @see Config
+ */
+ParsingResult parse(const char* path = DEFAULT_PATH);
+
+} // namespace engineConfig
+} // namespace android
diff --git a/services/audiopolicy/engine/config/src/EngineConfig.cpp b/services/audiopolicy/engine/config/src/EngineConfig.cpp
new file mode 100644
index 0000000..26dde66
--- /dev/null
+++ b/services/audiopolicy/engine/config/src/EngineConfig.cpp
@@ -0,0 +1,307 @@
+/*
+ * 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/Config"
+//#define LOG_NDEBUG 0
+
+#include "EngineConfig.h"
+#include <policy.h>
+#include <media/TypeConverter.h>
+#include <media/convert.h>
+#include <utils/Log.h>
+#include <libxml/parser.h>
+#include <libxml/xinclude.h>
+#include <string>
+#include <vector>
+#include <sstream>
+#include <istream>
+
+#include <cstdint>
+#include <string>
+
+
+namespace android {
+
+using utilities::convertTo;
+
+namespace engineConfig {
+
+static constexpr const char *gVersionAttribute = "version";
+static const char *const gReferenceElementName = "reference";
+static const char *const gReferenceAttributeName = "name";
+
+template<typename E, typename C>
+struct BaseSerializerTraits {
+ typedef E Element;
+ typedef C Collection;
+ typedef void* PtrSerializingCtx;
+};
+
+struct AttributesGroupTraits : public BaseSerializerTraits<AttributesGroup, AttributesGroups> {
+ static constexpr const char *tag = "AttributesGroup";
+ static constexpr const char *collectionTag = "AttributesGroups";
+
+ struct Attributes {
+ static constexpr const char *name = "name";
+ static constexpr const char *streamType = "streamType";
+ };
+ static android::status_t deserialize(_xmlDoc *doc, const _xmlNode *root, Collection &ps);
+};
+
+struct ProductStrategyTraits : public BaseSerializerTraits<ProductStrategy, ProductStrategies> {
+ static constexpr const char *tag = "ProductStrategy";
+ static constexpr const char *collectionTag = "ProductStrategies";
+
+ struct Attributes {
+ static constexpr const char *name = "name";
+ };
+ static android::status_t deserialize(_xmlDoc *doc, const _xmlNode *root, Collection &ps);
+};
+
+using xmlCharUnique = std::unique_ptr<xmlChar, decltype(xmlFree)>;
+
+std::string getXmlAttribute(const xmlNode *cur, const char *attribute)
+{
+ xmlCharUnique charPtr(xmlGetProp(cur, reinterpret_cast<const xmlChar *>(attribute)), xmlFree);
+ if (charPtr == NULL) {
+ return "";
+ }
+ std::string value(reinterpret_cast<const char*>(charPtr.get()));
+ return value;
+}
+
+static void getReference(const _xmlNode *root, const _xmlNode *&refNode, const std::string &refName,
+ const char *collectionTag)
+{
+ for (root = root->xmlChildrenNode; root != NULL; root = root->next) {
+ if (!xmlStrcmp(root->name, (const xmlChar *)collectionTag)) {
+ for (xmlNode *cur = root->xmlChildrenNode; cur != NULL; cur = cur->next) {
+ if ((!xmlStrcmp(cur->name, (const xmlChar *)gReferenceElementName))) {
+ std::string name = getXmlAttribute(cur, gReferenceAttributeName);
+ if (refName == name) {
+ refNode = cur;
+ return;
+ }
+ }
+ }
+ }
+ }
+ return;
+}
+
+template <class Trait>
+static status_t deserializeCollection(_xmlDoc *doc, const _xmlNode *cur,
+ typename Trait::Collection &collection,
+ size_t &nbSkippedElement)
+{
+ for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
+ if (xmlStrcmp(cur->name, (const xmlChar *)Trait::collectionTag) &&
+ xmlStrcmp(cur->name, (const xmlChar *)Trait::tag)) {
+ continue;
+ }
+ const xmlNode *child = cur;
+ if (!xmlStrcmp(child->name, (const xmlChar *)Trait::collectionTag)) {
+ child = child->xmlChildrenNode;
+ }
+ for (; child != NULL; child = child->next) {
+ if (!xmlStrcmp(child->name, (const xmlChar *)Trait::tag)) {
+ status_t status = Trait::deserialize(doc, child, collection);
+ if (status != NO_ERROR) {
+ nbSkippedElement += 1;
+ }
+ }
+ }
+ if (!xmlStrcmp(cur->name, (const xmlChar *)Trait::tag)) {
+ return NO_ERROR;
+ }
+ }
+ return NO_ERROR;
+}
+
+static constexpr const char *attributesAttributeRef = "attributesRef"; /**< for factorization. */
+
+static status_t parseAttributes(const _xmlNode *cur, audio_attributes_t &attributes)
+{
+ for (; cur != NULL; cur = cur->next) {
+ if (!xmlStrcmp(cur->name, (const xmlChar *)("ContentType"))) {
+ std::string contentTypeXml = getXmlAttribute(cur, "value");
+ audio_content_type_t contentType;
+ if (not AudioContentTypeConverter::fromString(contentTypeXml.c_str(), contentType)) {
+ ALOGE("Invalid content type %s", contentTypeXml.c_str());
+ return BAD_VALUE;
+ }
+ attributes.content_type = contentType;
+ ALOGV("%s content type %s", __FUNCTION__, contentTypeXml.c_str());
+ }
+ if (!xmlStrcmp(cur->name, (const xmlChar *)("Usage"))) {
+ std::string usageXml = getXmlAttribute(cur, "value");
+ audio_usage_t usage;
+ if (not UsageTypeConverter::fromString(usageXml.c_str(), usage)) {
+ ALOGE("Invalid usage %s", usageXml.c_str());
+ return BAD_VALUE;
+ }
+ attributes.usage = usage;
+ ALOGV("%s usage %s", __FUNCTION__, usageXml.c_str());
+ }
+ if (!xmlStrcmp(cur->name, (const xmlChar *)("Flags"))) {
+ std::string flags = getXmlAttribute(cur, "value");
+
+ ALOGV("%s flags %s", __FUNCTION__, flags.c_str());
+ attributes.flags = AudioFlagConverter::maskFromString(flags, " ");
+ }
+ if (!xmlStrcmp(cur->name, (const xmlChar *)("Bundle"))) {
+ std::string bundleKey = getXmlAttribute(cur, "key");
+ std::string bundleValue = getXmlAttribute(cur, "value");
+
+ ALOGV("%s Bundle %s %s", __FUNCTION__, bundleKey.c_str(), bundleValue.c_str());
+
+ std::string tags(bundleKey + "=" + bundleValue);
+ std::strncpy(attributes.tags, tags.c_str(), AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1);
+ }
+ }
+ return NO_ERROR;
+}
+
+static status_t deserializeAttributes(_xmlDoc *doc, const _xmlNode *cur,
+ audio_attributes_t &attributes) {
+ // Retrieve content type, usage, flags, and bundle from xml
+ for (; cur != NULL; cur = cur->next) {
+ if (not xmlStrcmp(cur->name, (const xmlChar *)("Attributes"))) {
+ const xmlNode *attrNode = cur;
+ std::string attrRef = getXmlAttribute(cur, attributesAttributeRef);
+ if (!attrRef.empty()) {
+ getReference(xmlDocGetRootElement(doc), attrNode, attrRef, attributesAttributeRef);
+ if (attrNode == NULL) {
+ ALOGE("%s: No reference found for %s", __FUNCTION__, attrRef.c_str());
+ return BAD_VALUE;
+ }
+ return deserializeAttributes(doc, attrNode->xmlChildrenNode, attributes);
+ }
+ return parseAttributes(attrNode->xmlChildrenNode, attributes);
+ }
+ if (not xmlStrcmp(cur->name, (const xmlChar *)("ContentType")) ||
+ not xmlStrcmp(cur->name, (const xmlChar *)("Usage")) ||
+ not xmlStrcmp(cur->name, (const xmlChar *)("Flags")) ||
+ not xmlStrcmp(cur->name, (const xmlChar *)("Bundle"))) {
+ return parseAttributes(cur, attributes);
+ }
+ }
+ return BAD_VALUE;
+}
+
+static status_t deserializeAttributesCollection(_xmlDoc *doc, const _xmlNode *cur,
+ AttributesVector &collection)
+{
+ status_t ret = BAD_VALUE;
+ // Either we do provide only one attributes or a collection of supported attributes
+ for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
+ if (not xmlStrcmp(cur->name, (const xmlChar *)("Attributes")) ||
+ not xmlStrcmp(cur->name, (const xmlChar *)("ContentType")) ||
+ not xmlStrcmp(cur->name, (const xmlChar *)("Usage")) ||
+ not xmlStrcmp(cur->name, (const xmlChar *)("Flags")) ||
+ not xmlStrcmp(cur->name, (const xmlChar *)("Bundle"))) {
+ audio_attributes_t attributes = AUDIO_ATTRIBUTES_INITIALIZER;
+ ret = deserializeAttributes(doc, cur, attributes);
+ if (ret == NO_ERROR) {
+ collection.push_back(attributes);
+ // We are done if the "Attributes" balise is omitted, only one Attributes is allowed
+ if (xmlStrcmp(cur->name, (const xmlChar *)("Attributes"))) {
+ return ret;
+ }
+ }
+ }
+ }
+ return ret;
+}
+
+status_t AttributesGroupTraits::deserialize(_xmlDoc *doc, const _xmlNode *child,
+ Collection &attributesGroup)
+{
+ std::string name = getXmlAttribute(child, Attributes::name);
+ if (name.empty()) {
+ ALOGV("AttributesGroupTraits No attribute %s found", Attributes::name);
+ }
+ ALOGV("%s: %s = %s", __FUNCTION__, Attributes::name, name.c_str());
+
+ audio_stream_type_t streamType = AUDIO_STREAM_DEFAULT;
+ std::string streamTypeXml = getXmlAttribute(child, Attributes::streamType);
+ if (streamTypeXml.empty()) {
+ ALOGV("%s: No attribute %s found", __FUNCTION__, Attributes::streamType);
+ } else {
+ ALOGV("%s: %s = %s", __FUNCTION__, Attributes::streamType, streamTypeXml.c_str());
+ if (not StreamTypeConverter::fromString(streamTypeXml.c_str(), streamType)) {
+ ALOGE("Invalid stream type %s", streamTypeXml.c_str());
+ return BAD_VALUE;
+ }
+ }
+ AttributesVector attributesVect;
+ deserializeAttributesCollection(doc, child, attributesVect);
+
+ attributesGroup.push_back({name, streamType, attributesVect});
+ return NO_ERROR;
+}
+
+status_t ProductStrategyTraits::deserialize(_xmlDoc *doc, const _xmlNode *child,
+ Collection &strategies)
+{
+ std::string name = getXmlAttribute(child, Attributes::name);
+ if (name.empty()) {
+ ALOGE("ProductStrategyTraits No attribute %s found", Attributes::name);
+ return BAD_VALUE;
+ }
+ ALOGV("%s: %s = %s", __FUNCTION__, Attributes::name, name.c_str());
+
+ size_t skipped = 0;
+ AttributesGroups attrGroups;
+ deserializeCollection<AttributesGroupTraits>(doc, child, attrGroups, skipped);
+
+ strategies.push_back({name, attrGroups});
+ return NO_ERROR;
+}
+
+ParsingResult parse(const char* path) {
+ xmlDocPtr doc;
+ doc = xmlParseFile(path);
+ if (doc == NULL) {
+ ALOGE("%s: Could not parse document %s", __FUNCTION__, path);
+ return {nullptr, 0};
+ }
+ xmlNodePtr cur = xmlDocGetRootElement(doc);
+ if (cur == NULL) {
+ ALOGE("%s: Could not parse: empty document %s", __FUNCTION__, path);
+ xmlFreeDoc(doc);
+ return {nullptr, 0};
+ }
+ if (xmlXIncludeProcess(doc) < 0) {
+ ALOGE("%s: libxml failed to resolve XIncludes on document %s", __FUNCTION__, path);
+ return {nullptr, 0};
+ }
+ std::string version = getXmlAttribute(cur, gVersionAttribute);
+ if (version.empty()) {
+ ALOGE("%s: No version found", __func__);
+ return {nullptr, 0};
+ }
+ size_t nbSkippedElements = 0;
+ auto config = std::make_unique<Config>();
+ config->version = std::stof(version);
+ deserializeCollection<ProductStrategyTraits>(
+ doc, cur, config->productStrategies, nbSkippedElements);
+
+ return {std::move(config), nbSkippedElements};
+}
+
+} // namespace engineConfig
+} // namespace android
diff --git a/services/audiopolicy/engine/interface/Android.bp b/services/audiopolicy/engine/interface/Android.bp
new file mode 100644
index 0000000..2ea42b6
--- /dev/null
+++ b/services/audiopolicy/engine/interface/Android.bp
@@ -0,0 +1,19 @@
+// 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.
+
+cc_library_headers {
+ name: "libaudiopolicyengine_interface_headers",
+ host_supported: true,
+ export_include_dirs: ["."],
+}
diff --git a/services/audiopolicy/engine/interface/AudioPolicyManagerInterface.h b/services/audiopolicy/engine/interface/AudioPolicyManagerInterface.h
index 04594f5..9f5fb0c2 100644
--- a/services/audiopolicy/engine/interface/AudioPolicyManagerInterface.h
+++ b/services/audiopolicy/engine/interface/AudioPolicyManagerInterface.h
@@ -18,6 +18,8 @@
#include <AudioPolicyManagerObserver.h>
#include <RoutingStrategy.h>
+#include <media/AudioCommonTypes.h>
+#include <policy.h>
#include <Volume.h>
#include <HwModule.h>
#include <DeviceDescriptor.h>
@@ -28,6 +30,10 @@
namespace android {
+using DeviceStrategyMap = std::map<product_strategy_t, DeviceVector>;
+using StrategyVector = std::vector<product_strategy_t>;
+
+
/**
* This interface is dedicated to the policy manager that a Policy Engine shall implement.
*/
@@ -55,6 +61,8 @@
* @param[in] inputSource to get the selected input device associated to
*
* @return selected input device for the given input source, may be none if error.
+ *
+ * @deprecated use getInputDeviceForAttributes
*/
virtual audio_devices_t getDeviceForInputSource(audio_source_t inputSource) const = 0;
@@ -64,6 +72,8 @@
* @param[in] stream type for which the selected ouput device is requested.
*
* @return selected ouput device for the given strategy, may be none if error.
+ *
+ * @deprecated use getOutputDeviceForAttributes
*/
virtual audio_devices_t getDeviceForStrategy(routing_strategy stategy) const = 0;
@@ -73,6 +83,9 @@
* @param[in] stream: for which the selected strategy followed by is requested.
*
* @return strategy to be followed.
+ *
+ * @deprecated use getOrderedStreams() / getLinkedStreams() to apply operation on stream
+ * following same former routing_strategy
*/
virtual routing_strategy getStrategyForStream(audio_stream_type_t stream) = 0;
@@ -82,6 +95,8 @@
* @param[in] usage to get the selected strategy followed by.
*
* @return strategy to be followed.
+ *
+ * @deprecated use getProductStrategyForAttributes
*/
virtual routing_strategy getStrategyForUsage(audio_usage_t usage) = 0;
@@ -133,6 +148,130 @@
virtual status_t setDeviceConnectionState(const android::sp<android::DeviceDescriptor> devDesc,
audio_policy_dev_state_t state) = 0;
+ /**
+ * Get the strategy selected for a given audio attributes.
+ *
+ * @param[in] audio attributes to get the selected @product_strategy_t followed by.
+ *
+ * @return @product_strategy_t to be followed.
+ */
+ virtual product_strategy_t getProductStrategyForAttributes(
+ const audio_attributes_t &attr) const = 0;
+
+ /**
+ * @brief getOutputDevicesForAttributes retrieves the devices to be used for given
+ * audio attributes.
+ * @param attributes of the output requesting Device(s) selection
+ * @param preferedDevice valid reference if a prefered device is requested, nullptr otherwise.
+ * @param fromCache if true, the device is returned from internal cache,
+ * otherwise it is determined by current state (device connected,phone state,
+ * force use, a2dp output...)
+ * @return vector of selected device descriptors.
+ * Appropriate device for streams handled by the specified audio attributes according
+ * to current phone state, forced states, connected devices...
+ * if fromCache is true, the device is returned from internal cache,
+ * otherwise it is determined by current state (device connected,phone state, force use,
+ * a2dp output...)
+ * This allows to:
+ * 1 speed up process when the state is stable (when starting or stopping an output)
+ * 2 access to either current device selection (fromCache == true) or
+ * "future" device selection (fromCache == false) when called from a context
+ * where conditions are changing (setDeviceConnectionState(), setPhoneState()...) AND
+ * before manager updates its outputs.
+ */
+ virtual DeviceVector getOutputDevicesForAttributes(
+ const audio_attributes_t &attributes,
+ const sp<DeviceDescriptor> &preferedDevice = nullptr,
+ bool fromCache = false) const = 0;
+
+ /**
+ * @brief getOutputDevicesForStream Legacy function retrieving devices from a stream type.
+ * @param stream type of the output requesting Device(s) selection
+ * @param fromCache if true, the device is returned from internal cache,
+ * otherwise it is determined by current state (device connected,phone state,
+ * force use, a2dp output...)
+ * @return appropriate device for streams handled by the specified audio attributes according
+ * to current phone state, forced states, connected devices...
+ * if fromCache is true, the device is returned from internal cache,
+ * otherwise it is determined by current state (device connected,phone state, force use,
+ * a2dp output...)
+ * This allows to:
+ * 1 speed up process when the state is stable (when starting or stopping an output)
+ * 2 access to either current device selection (fromCache == true) or
+ * "future" device selection (fromCache == false) when called from a context
+ * where conditions are changing (setDeviceConnectionState(), setPhoneState()...) AND
+ * before manager updates its outputs.
+ */
+ virtual DeviceVector getOutputDevicesForStream(audio_stream_type_t stream,
+ bool fromCache = false) const = 0;
+
+ /**
+ * Get the input device selected for given audio attributes.
+ *
+ * @param[in] attr audio attributes to consider
+ * @param[out] mix to be used if a mix has been installed for the given audio attributes.
+ * @return selected input device for the audio attributes, may be null if error.
+ */
+ virtual sp<DeviceDescriptor> getInputDeviceForAttributes(
+ const audio_attributes_t &attr, AudioMix **mix = nullptr) const = 0;
+
+ /**
+ * Get the legacy stream type for a given audio attributes.
+ *
+ * @param[in] audio attributes to get the associated audio_stream_type_t.
+ *
+ * @return audio_stream_type_t associated to the attributes.
+ */
+ virtual audio_stream_type_t getStreamTypeForAttributes(
+ const audio_attributes_t &attr) const = 0;
+
+ /**
+ * @brief getAttributesForStream get the audio attributes from legacy stream type
+ * @param stream to consider
+ * @return audio attributes matching the legacy stream type
+ */
+ virtual audio_attributes_t getAttributesForStreamType(audio_stream_type_t stream) const = 0;
+
+ /**
+ * @brief getStreamTypesForProductStrategy retrieves the list of legacy stream type following
+ * the given product strategy
+ * @param ps product strategy to consider
+ * @return associated legacy Stream Types vector of the given product strategy
+ */
+ virtual StreamTypeVector getStreamTypesForProductStrategy(product_strategy_t ps) const = 0;
+
+ /**
+ * @brief getAllAttributesForProductStrategy retrieves all the attributes following the given
+ * product strategy. Any attributes that "matches" with this one will follow the product
+ * strategy.
+ * "matching" means the usage shall match if reference attributes has a defined usage, AND
+ * content type shall match if reference attributes has a defined content type AND
+ * flags shall match if reference attributes has defined flags AND
+ * tags shall match if reference attributes has defined tags.
+ * @param ps product strategy to consider
+ * @return vector of product strategy ids, empty if unknown strategy.
+ */
+ virtual AttributesVector getAllAttributesForProductStrategy(product_strategy_t ps) const = 0;
+
+ /**
+ * @brief getOrderedAudioProductStrategies
+ * @return priority ordered product strategies to help the AudioPolicyManager evaluating the
+ * device selection per output according to the prioritized strategies.
+ */
+ virtual StrategyVector getOrderedProductStrategies() const = 0;
+
+ /**
+ * @brief updateDeviceSelectionCache. Device selection for AudioAttribute / Streams is cached
+ * in the engine in order to speed up process when the audio system is stable.
+ * When a device is connected, the android mode is changed, engine is notified and can update
+ * the cache.
+ * When starting / stopping an output with a stream that can affect notification, the engine
+ * needs to update the cache upon this function call.
+ */
+ virtual void updateDeviceSelectionCache() = 0;
+
+ virtual void dump(String8 *dst) const = 0;
+
protected:
virtual ~AudioPolicyManagerInterface() {}
};