blob: caa1ec92529e2df20c8453de5e58b2be70f2809e [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
240 return result.nbSkippedElement == 0? NO_ERROR : BAD_VALUE;
241}
242
243DeviceVector Engine::getDevicesForProductStrategy(product_strategy_t ps) const
244{
245 const auto productStrategies = getProductStrategies();
246 if (productStrategies.find(ps) == productStrategies.end()) {
247 ALOGE("%s: Trying to get device on invalid strategy %d", __FUNCTION__, ps);
248 return {};
249 }
250 const DeviceVector &availableOutputDevices = getApmObserver()->getAvailableOutputDevices();
251 const SwAudioOutputCollection &outputs = getApmObserver()->getOutputs();
252 uint32_t availableOutputDevicesType = availableOutputDevices.types();
253
254 /** This is the only case handled programmatically because the PFW is unable to know the
255 * activity of streams.
256 *
257 * -While media is playing on a remote device, use the the sonification behavior.
258 * Note that we test this usecase before testing if media is playing because
259 * the isStreamActive() method only informs about the activity of a stream, not
260 * if it's for local playback. Note also that we use the same delay between both tests
261 *
262 * -When media is not playing anymore, fall back on the sonification behavior
263 */
264 audio_devices_t devices = AUDIO_DEVICE_NONE;
265 if (ps == getProductStrategyForStream(AUDIO_STREAM_NOTIFICATION) &&
266 !is_state_in_call(getPhoneState()) &&
267 !outputs.isStreamActiveRemotely(AUDIO_STREAM_MUSIC,
268 SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY) &&
269 outputs.isStreamActive(AUDIO_STREAM_MUSIC,
270 SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY)) {
271 product_strategy_t strategyForMedia =
272 getProductStrategyForStream(AUDIO_STREAM_MUSIC);
273 devices = productStrategies.getDeviceTypesForProductStrategy(strategyForMedia);
274 } else if (ps == getProductStrategyForStream(AUDIO_STREAM_ACCESSIBILITY) &&
275 (outputs.isStreamActive(AUDIO_STREAM_RING) ||
276 outputs.isStreamActive(AUDIO_STREAM_ALARM))) {
277 // do not route accessibility prompts to a digital output currently configured with a
278 // compressed format as they would likely not be mixed and dropped.
279 // Device For Sonification conf file has HDMI, SPDIF and HDMI ARC unreacheable.
280 product_strategy_t strategyNotification = getProductStrategyForStream(AUDIO_STREAM_RING);
281 devices = productStrategies.getDeviceTypesForProductStrategy(strategyNotification);
282 } else {
283 devices = productStrategies.getDeviceTypesForProductStrategy(ps);
284 }
285 if (devices == AUDIO_DEVICE_NONE ||
286 (devices & availableOutputDevicesType) == AUDIO_DEVICE_NONE) {
287 devices = getApmObserver()->getDefaultOutputDevice()->type();
288 ALOGE_IF(devices == AUDIO_DEVICE_NONE, "%s: no valid default device defined", __FUNCTION__);
289 return DeviceVector(getApmObserver()->getDefaultOutputDevice());
290 }
291 if (/*device_distinguishes_on_address(devices)*/ devices == AUDIO_DEVICE_OUT_BUS) {
292 // We do expect only one device for these types of devices
293 // Criterion device address garantee this one is available
294 // If this criterion is not wished, need to ensure this device is available
295 const String8 address(productStrategies.getDeviceAddressForProductStrategy(ps).c_str());
296 ALOGV("%s:device 0x%x %s %d", __FUNCTION__, devices, address.c_str(), ps);
297 return DeviceVector(availableOutputDevices.getDevice(devices,
298 address,
299 AUDIO_FORMAT_DEFAULT));
300 }
301 ALOGV("%s:device 0x%x %d", __FUNCTION__, devices, ps);
302 return availableOutputDevices.getDevicesFromTypeMask(devices);
303}
304
305DeviceVector Engine::getOutputDevicesForAttributes(const audio_attributes_t &attributes,
306 const sp<DeviceDescriptor> &preferredDevice,
307 bool fromCache) const
308{
309 // First check for explict routing device
310 if (preferredDevice != nullptr) {
311 ALOGV("%s explicit Routing on device %s", __func__, preferredDevice->toString().c_str());
312 return DeviceVector(preferredDevice);
313 }
314 product_strategy_t strategy = EngineBase::getProductStrategyForAttributes(attributes);
315 //
316 // @TODO: manage dynamic mix
317 //
318 return fromCache? mDevicesForStrategies.at(strategy) : getDevicesForProductStrategy(strategy);
319}
320
321DeviceVector Engine::getOutputDevicesForStream(audio_stream_type_t stream, bool fromCache) const
322{
323 auto attributes = EngineBase::getAttributesForStreamType(stream);
324 return getOutputDevicesForAttributes(attributes, nullptr, fromCache);
325}
326
327sp<DeviceDescriptor> Engine::getInputDeviceForAttributes(const audio_attributes_t &attr,
328 AudioMix **/*mix*/) const
329{
330 const auto &availInputDevices = getApmObserver()->getAvailableInputDevices();
331 std::string address;
332 //
333 // @TODO: manage explicit routing and dynamic mix
334 //
335 audio_devices_t deviceType = getPropertyForKey<audio_devices_t, audio_source_t>(attr.source);
336
337 if (audio_is_remote_submix_device(deviceType)) {
338 address = "0";
339 std::size_t pos;
340 std::string tags { attr.tags };
341 if ((pos = tags.find("addr=")) != std::string::npos) {
342 address = tags.substr(pos + std::strlen("addr="));
343 }
344 }
345 return availInputDevices.getDevice(deviceType, String8(address.c_str()), AUDIO_FORMAT_DEFAULT);
346}
347
348void Engine::updateDeviceSelectionCache()
349{
350 for (const auto &iter : getProductStrategies()) {
351 const auto &strategy = iter.second;
352 mDevicesForStrategies[strategy->getId()] = getDevicesForProductStrategy(strategy->getId());
353 }
354}
355
François Gaffief1e95082018-11-02 13:53:31 +0100356void Engine::setDeviceAddressForProductStrategy(product_strategy_t strategy,
357 const std::string &address)
358{
359 if (getProductStrategies().find(strategy) == getProductStrategies().end()) {
360 ALOGE("%s: Trying to set address %s on invalid strategy %d", __FUNCTION__, address.c_str(),
361 strategy);
362 return;
363 }
364 getProductStrategies().at(strategy)->setDeviceAddress(address);
365}
366
367bool Engine::setDeviceTypesForProductStrategy(product_strategy_t strategy, audio_devices_t devices)
368{
369 if (getProductStrategies().find(strategy) == getProductStrategies().end()) {
370 ALOGE("%s: set device %d on invalid strategy %d", __FUNCTION__, devices, strategy);
371 return false;
372 }
373 getProductStrategies().at(strategy)->setDeviceTypes(devices);
374 return true;
375}
376
François Gaffie20f06f92015-03-24 09:01:14 +0100377template <>
378AudioPolicyManagerInterface *Engine::queryInterface()
379{
François Gaffiedc7553f2018-11-02 10:39:57 +0100380 return this;
François Gaffie20f06f92015-03-24 09:01:14 +0100381}
382
383template <>
384AudioPolicyPluginInterface *Engine::queryInterface()
385{
François Gaffiedc7553f2018-11-02 10:39:57 +0100386 return this;
François Gaffie20f06f92015-03-24 09:01:14 +0100387}
388
389} // namespace audio_policy
390} // namespace android
391
392