blob: a04254c450cef39e3874bb4deef1b65c3b56f4f0 [file] [log] [blame]
François Gaffie20f06f92015-03-24 09:01:14 +01001/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_TAG "APM::AudioPolicyEngine"
18//#define LOG_NDEBUG 0
19
20//#define VERY_VERBOSE_LOGGING
21#ifdef VERY_VERBOSE_LOGGING
22#define ALOGVV ALOGV
23#else
24#define ALOGVV(a...) do { } while(0)
25#endif
26
27#include "Engine.h"
28#include "Strategy.h"
29#include "Stream.h"
30#include "InputSource.h"
31#include "Usage.h"
François Gaffiedc7553f2018-11-02 10:39:57 +010032
33#include <EngineConfig.h>
François Gaffie20f06f92015-03-24 09:01:14 +010034#include <policy.h>
35#include <ParameterManagerWrapper.h>
36
37using std::string;
38using std::map;
39
François Gaffief19cf792018-05-30 17:22:17 +020040namespace android {
41namespace audio_policy {
42
François Gaffie20f06f92015-03-24 09:01:14 +010043template <>
44StrategyCollection &Engine::getCollection<routing_strategy>()
45{
46 return mStrategyCollection;
47}
48template <>
49StreamCollection &Engine::getCollection<audio_stream_type_t>()
50{
51 return mStreamCollection;
52}
53template <>
54UsageCollection &Engine::getCollection<audio_usage_t>()
55{
56 return mUsageCollection;
57}
58template <>
59InputSourceCollection &Engine::getCollection<audio_source_t>()
60{
61 return mInputSourceCollection;
62}
63
64template <>
65const StrategyCollection &Engine::getCollection<routing_strategy>() const
66{
67 return mStrategyCollection;
68}
69template <>
70const StreamCollection &Engine::getCollection<audio_stream_type_t>() const
71{
72 return mStreamCollection;
73}
74template <>
75const UsageCollection &Engine::getCollection<audio_usage_t>() const
76{
77 return mUsageCollection;
78}
79template <>
80const InputSourceCollection &Engine::getCollection<audio_source_t>() const
81{
82 return mInputSourceCollection;
83}
84
François Gaffiedc7553f2018-11-02 10:39:57 +010085Engine::Engine() : mPolicyParameterMgr(new ParameterManagerWrapper())
François Gaffie20f06f92015-03-24 09:01:14 +010086{
François Gaffiedc7553f2018-11-02 10:39:57 +010087 status_t loadResult = loadAudioPolicyEngineConfig();
88 if (loadResult < 0) {
89 ALOGE("Policy Engine configuration is invalid.");
90 }
François Gaffie20f06f92015-03-24 09:01:14 +010091}
92
93Engine::~Engine()
94{
95 mStrategyCollection.clear();
96 mStreamCollection.clear();
97 mInputSourceCollection.clear();
98 mUsageCollection.clear();
99}
100
François Gaffie20f06f92015-03-24 09:01:14 +0100101status_t Engine::initCheck()
102{
François Gaffiedc7553f2018-11-02 10:39:57 +0100103 if (mPolicyParameterMgr == nullptr || mPolicyParameterMgr->start() != NO_ERROR) {
François Gaffie0f17ab72015-05-13 18:13:00 +0200104 ALOGE("%s: could not start Policy PFW", __FUNCTION__);
François Gaffie0f17ab72015-05-13 18:13:00 +0200105 return NO_INIT;
106 }
François Gaffiedc7553f2018-11-02 10:39:57 +0100107 return EngineBase::initCheck();
François Gaffie20f06f92015-03-24 09:01:14 +0100108}
109
François Gaffie20f06f92015-03-24 09:01:14 +0100110template <typename Key>
111Element<Key> *Engine::getFromCollection(const Key &key) const
112{
113 const Collection<Key> collection = getCollection<Key>();
114 return collection.get(key);
115}
116
117template <typename Key>
118status_t Engine::add(const std::string &name, const Key &key)
119{
120 Collection<Key> &collection = getCollection<Key>();
121 return collection.add(name, key);
122}
123
François Gaffie20f06f92015-03-24 09:01:14 +0100124template <typename Property, typename Key>
125Property Engine::getPropertyForKey(Key key) const
126{
127 Element<Key> *element = getFromCollection<Key>(key);
128 if (element == NULL) {
129 ALOGE("%s: Element not found within collection", __FUNCTION__);
130 return static_cast<Property>(0);
131 }
132 return element->template get<Property>();
133}
134
François Gaffiedc7553f2018-11-02 10:39:57 +0100135routing_strategy Engine::getStrategyForUsage(audio_usage_t usage)
François Gaffie20f06f92015-03-24 09:01:14 +0100136{
François Gaffiedc7553f2018-11-02 10:39:57 +0100137 return getPropertyForKey<routing_strategy, audio_usage_t>(usage);
François Gaffie0f17ab72015-05-13 18:13:00 +0200138}
139
François Gaffiedc7553f2018-11-02 10:39:57 +0100140audio_devices_t Engine::getDeviceForStrategy(routing_strategy strategy) const
François Gaffie0f17ab72015-05-13 18:13:00 +0200141{
François Gaffiedc7553f2018-11-02 10:39:57 +0100142 const SwAudioOutputCollection &outputs = getApmObserver()->getOutputs();
François Gaffie20f06f92015-03-24 09:01:14 +0100143
144 /** This is the only case handled programmatically because the PFW is unable to know the
145 * activity of streams.
146 *
147 * -While media is playing on a remote device, use the the sonification behavior.
148 * Note that we test this usecase before testing if media is playing because
149 * the isStreamActive() method only informs about the activity of a stream, not
150 * if it's for local playback. Note also that we use the same delay between both tests
151 *
152 * -When media is not playing anymore, fall back on the sonification behavior
153 */
154 if (strategy == STRATEGY_SONIFICATION_RESPECTFUL &&
155 !is_state_in_call(getPhoneState()) &&
156 !outputs.isStreamActiveRemotely(AUDIO_STREAM_MUSIC,
157 SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY) &&
158 outputs.isStreamActive(AUDIO_STREAM_MUSIC, SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY)) {
François Gaffiedc7553f2018-11-02 10:39:57 +0100159 return getPropertyForKey<audio_devices_t, routing_strategy>(STRATEGY_MEDIA);
François Gaffie20f06f92015-03-24 09:01:14 +0100160 }
François Gaffie4ac9d842016-04-12 16:56:35 +0200161 if (strategy == STRATEGY_ACCESSIBILITY &&
162 (outputs.isStreamActive(AUDIO_STREAM_RING) || outputs.isStreamActive(AUDIO_STREAM_ALARM))) {
163 // do not route accessibility prompts to a digital output currently configured with a
164 // compressed format as they would likely not be mixed and dropped.
165 // Device For Sonification conf file has HDMI, SPDIF and HDMI ARC unreacheable.
François Gaffiedc7553f2018-11-02 10:39:57 +0100166 return getPropertyForKey<audio_devices_t, routing_strategy>(STRATEGY_SONIFICATION);
François Gaffie4ac9d842016-04-12 16:56:35 +0200167 }
François Gaffiedc7553f2018-11-02 10:39:57 +0100168 return getPropertyForKey<audio_devices_t, routing_strategy>(strategy);
François Gaffie20f06f92015-03-24 09:01:14 +0100169}
170
François Gaffiedc7553f2018-11-02 10:39:57 +0100171bool Engine::setVolumeProfileForStream(const audio_stream_type_t &stream,
172 const audio_stream_type_t &profile)
François Gaffied1ab2bd2015-12-02 18:20:06 +0100173{
François Gaffiedc7553f2018-11-02 10:39:57 +0100174 if (setPropertyForKey<audio_stream_type_t, audio_stream_type_t>(stream, profile)) {
175 getApmObserver()->getVolumeCurves().switchVolumeCurve(profile, stream);
François Gaffied1ab2bd2015-12-02 18:20:06 +0100176 return true;
177 }
178 return false;
179}
180
François Gaffie20f06f92015-03-24 09:01:14 +0100181template <typename Property, typename Key>
182bool Engine::setPropertyForKey(const Property &property, const Key &key)
183{
184 Element<Key> *element = getFromCollection<Key>(key);
185 if (element == NULL) {
186 ALOGE("%s: Element not found within collection", __FUNCTION__);
187 return BAD_VALUE;
188 }
189 return element->template set<Property>(property) == NO_ERROR;
190}
191
François Gaffie20f06f92015-03-24 09:01:14 +0100192status_t Engine::setPhoneState(audio_mode_t mode)
193{
François Gaffiedc7553f2018-11-02 10:39:57 +0100194 status_t status = mPolicyParameterMgr->setPhoneState(mode);
195 if (status != NO_ERROR) {
196 return status;
197 }
198 return EngineBase::setPhoneState(mode);
François Gaffie20f06f92015-03-24 09:01:14 +0100199}
200
201audio_mode_t Engine::getPhoneState() const
202{
203 return mPolicyParameterMgr->getPhoneState();
204}
205
206status_t Engine::setForceUse(audio_policy_force_use_t usage,
207 audio_policy_forced_cfg_t config)
208{
François Gaffiedc7553f2018-11-02 10:39:57 +0100209 status_t status = mPolicyParameterMgr->setForceUse(usage, config);
210 if (status != NO_ERROR) {
211 return status;
212 }
213 return EngineBase::setForceUse(usage, config);
François Gaffie20f06f92015-03-24 09:01:14 +0100214}
215
216audio_policy_forced_cfg_t Engine::getForceUse(audio_policy_force_use_t usage) const
217{
218 return mPolicyParameterMgr->getForceUse(usage);
219}
220
François Gaffiea3e696d2015-12-18 09:38:43 +0100221status_t Engine::setDeviceConnectionState(const sp<DeviceDescriptor> devDesc,
François Gaffie3305c112018-02-22 10:56:49 +0100222 audio_policy_dev_state_t state)
François Gaffie20f06f92015-03-24 09:01:14 +0100223{
François Gaffie3305c112018-02-22 10:56:49 +0100224 mPolicyParameterMgr->setDeviceConnectionState(devDesc, state);
225
François Gaffiea3e696d2015-12-18 09:38:43 +0100226 if (audio_is_output_device(devDesc->type())) {
227 return mPolicyParameterMgr->setAvailableOutputDevices(
François Gaffiedc7553f2018-11-02 10:39:57 +0100228 getApmObserver()->getAvailableOutputDevices().types());
François Gaffiea3e696d2015-12-18 09:38:43 +0100229 } else if (audio_is_input_device(devDesc->type())) {
230 return mPolicyParameterMgr->setAvailableInputDevices(
François Gaffiedc7553f2018-11-02 10:39:57 +0100231 getApmObserver()->getAvailableInputDevices().types());
François Gaffiea3e696d2015-12-18 09:38:43 +0100232 }
233 return BAD_TYPE;
François Gaffie20f06f92015-03-24 09:01:14 +0100234}
235
François Gaffiedc7553f2018-11-02 10:39:57 +0100236status_t Engine::loadAudioPolicyEngineConfig()
237{
238 auto result = EngineBase::loadAudioPolicyEngineConfig();
239
François Gaffie7188f1a2018-11-02 14:35:42 +0100240 // Custom XML Parsing
241 auto loadCriteria= [this](const auto& configCriteria, const auto& configCriterionTypes) {
242 for (auto& criterion : configCriteria) {
243 engineConfig::CriterionType criterionType;
244 for (auto &configCriterionType : configCriterionTypes) {
245 if (configCriterionType.name == criterion.typeName) {
246 criterionType = configCriterionType;
247 break;
248 }
249 }
250 ALOG_ASSERT(not criterionType.name.empty(), "Invalid criterion type for %s",
251 criterion.name.c_str());
252 mPolicyParameterMgr->addCriterion(criterion.name, criterionType.isInclusive,
253 criterionType.valuePairs,
254 criterion.defaultLiteralValue);
255 }
256 };
257
258 loadCriteria(result.parsedConfig->criteria, result.parsedConfig->criterionTypes);
François Gaffiedc7553f2018-11-02 10:39:57 +0100259 return result.nbSkippedElement == 0? NO_ERROR : BAD_VALUE;
260}
261
262DeviceVector Engine::getDevicesForProductStrategy(product_strategy_t ps) const
263{
264 const auto productStrategies = getProductStrategies();
265 if (productStrategies.find(ps) == productStrategies.end()) {
266 ALOGE("%s: Trying to get device on invalid strategy %d", __FUNCTION__, ps);
267 return {};
268 }
269 const DeviceVector &availableOutputDevices = getApmObserver()->getAvailableOutputDevices();
270 const SwAudioOutputCollection &outputs = getApmObserver()->getOutputs();
271 uint32_t availableOutputDevicesType = availableOutputDevices.types();
272
273 /** This is the only case handled programmatically because the PFW is unable to know the
274 * activity of streams.
275 *
276 * -While media is playing on a remote device, use the the sonification behavior.
277 * Note that we test this usecase before testing if media is playing because
278 * the isStreamActive() method only informs about the activity of a stream, not
279 * if it's for local playback. Note also that we use the same delay between both tests
280 *
281 * -When media is not playing anymore, fall back on the sonification behavior
282 */
283 audio_devices_t devices = AUDIO_DEVICE_NONE;
284 if (ps == getProductStrategyForStream(AUDIO_STREAM_NOTIFICATION) &&
285 !is_state_in_call(getPhoneState()) &&
286 !outputs.isStreamActiveRemotely(AUDIO_STREAM_MUSIC,
287 SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY) &&
288 outputs.isStreamActive(AUDIO_STREAM_MUSIC,
289 SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY)) {
290 product_strategy_t strategyForMedia =
291 getProductStrategyForStream(AUDIO_STREAM_MUSIC);
292 devices = productStrategies.getDeviceTypesForProductStrategy(strategyForMedia);
293 } else if (ps == getProductStrategyForStream(AUDIO_STREAM_ACCESSIBILITY) &&
294 (outputs.isStreamActive(AUDIO_STREAM_RING) ||
295 outputs.isStreamActive(AUDIO_STREAM_ALARM))) {
296 // do not route accessibility prompts to a digital output currently configured with a
297 // compressed format as they would likely not be mixed and dropped.
298 // Device For Sonification conf file has HDMI, SPDIF and HDMI ARC unreacheable.
299 product_strategy_t strategyNotification = getProductStrategyForStream(AUDIO_STREAM_RING);
300 devices = productStrategies.getDeviceTypesForProductStrategy(strategyNotification);
301 } else {
302 devices = productStrategies.getDeviceTypesForProductStrategy(ps);
303 }
304 if (devices == AUDIO_DEVICE_NONE ||
305 (devices & availableOutputDevicesType) == AUDIO_DEVICE_NONE) {
306 devices = getApmObserver()->getDefaultOutputDevice()->type();
307 ALOGE_IF(devices == AUDIO_DEVICE_NONE, "%s: no valid default device defined", __FUNCTION__);
308 return DeviceVector(getApmObserver()->getDefaultOutputDevice());
309 }
310 if (/*device_distinguishes_on_address(devices)*/ devices == AUDIO_DEVICE_OUT_BUS) {
311 // We do expect only one device for these types of devices
312 // Criterion device address garantee this one is available
313 // If this criterion is not wished, need to ensure this device is available
314 const String8 address(productStrategies.getDeviceAddressForProductStrategy(ps).c_str());
315 ALOGV("%s:device 0x%x %s %d", __FUNCTION__, devices, address.c_str(), ps);
316 return DeviceVector(availableOutputDevices.getDevice(devices,
317 address,
318 AUDIO_FORMAT_DEFAULT));
319 }
320 ALOGV("%s:device 0x%x %d", __FUNCTION__, devices, ps);
321 return availableOutputDevices.getDevicesFromTypeMask(devices);
322}
323
324DeviceVector Engine::getOutputDevicesForAttributes(const audio_attributes_t &attributes,
325 const sp<DeviceDescriptor> &preferredDevice,
326 bool fromCache) const
327{
328 // First check for explict routing device
329 if (preferredDevice != nullptr) {
330 ALOGV("%s explicit Routing on device %s", __func__, preferredDevice->toString().c_str());
331 return DeviceVector(preferredDevice);
332 }
333 product_strategy_t strategy = EngineBase::getProductStrategyForAttributes(attributes);
334 //
335 // @TODO: manage dynamic mix
336 //
337 return fromCache? mDevicesForStrategies.at(strategy) : getDevicesForProductStrategy(strategy);
338}
339
340DeviceVector Engine::getOutputDevicesForStream(audio_stream_type_t stream, bool fromCache) const
341{
342 auto attributes = EngineBase::getAttributesForStreamType(stream);
343 return getOutputDevicesForAttributes(attributes, nullptr, fromCache);
344}
345
346sp<DeviceDescriptor> Engine::getInputDeviceForAttributes(const audio_attributes_t &attr,
347 AudioMix **/*mix*/) const
348{
349 const auto &availInputDevices = getApmObserver()->getAvailableInputDevices();
350 std::string address;
351 //
352 // @TODO: manage explicit routing and dynamic mix
353 //
354 audio_devices_t deviceType = getPropertyForKey<audio_devices_t, audio_source_t>(attr.source);
355
356 if (audio_is_remote_submix_device(deviceType)) {
357 address = "0";
358 std::size_t pos;
359 std::string tags { attr.tags };
360 if ((pos = tags.find("addr=")) != std::string::npos) {
361 address = tags.substr(pos + std::strlen("addr="));
362 }
363 }
364 return availInputDevices.getDevice(deviceType, String8(address.c_str()), AUDIO_FORMAT_DEFAULT);
365}
366
367void Engine::updateDeviceSelectionCache()
368{
369 for (const auto &iter : getProductStrategies()) {
370 const auto &strategy = iter.second;
371 mDevicesForStrategies[strategy->getId()] = getDevicesForProductStrategy(strategy->getId());
372 }
373}
374
François Gaffief1e95082018-11-02 13:53:31 +0100375void Engine::setDeviceAddressForProductStrategy(product_strategy_t strategy,
376 const std::string &address)
377{
378 if (getProductStrategies().find(strategy) == getProductStrategies().end()) {
379 ALOGE("%s: Trying to set address %s on invalid strategy %d", __FUNCTION__, address.c_str(),
380 strategy);
381 return;
382 }
383 getProductStrategies().at(strategy)->setDeviceAddress(address);
384}
385
386bool Engine::setDeviceTypesForProductStrategy(product_strategy_t strategy, audio_devices_t devices)
387{
388 if (getProductStrategies().find(strategy) == getProductStrategies().end()) {
389 ALOGE("%s: set device %d on invalid strategy %d", __FUNCTION__, devices, strategy);
390 return false;
391 }
392 getProductStrategies().at(strategy)->setDeviceTypes(devices);
393 return true;
394}
395
François Gaffie20f06f92015-03-24 09:01:14 +0100396template <>
397AudioPolicyManagerInterface *Engine::queryInterface()
398{
François Gaffiedc7553f2018-11-02 10:39:57 +0100399 return this;
François Gaffie20f06f92015-03-24 09:01:14 +0100400}
401
402template <>
403AudioPolicyPluginInterface *Engine::queryInterface()
404{
François Gaffiedc7553f2018-11-02 10:39:57 +0100405 return this;
François Gaffie20f06f92015-03-24 09:01:14 +0100406}
407
408} // namespace audio_policy
409} // namespace android
410
411