blob: f07ce824f42831a4a759b6b9a629378b881b2ea8 [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"
François Gaffie20f06f92015-03-24 09:01:14 +010028#include "Stream.h"
29#include "InputSource.h"
François Gaffiedc7553f2018-11-02 10:39:57 +010030
31#include <EngineConfig.h>
François Gaffie20f06f92015-03-24 09:01:14 +010032#include <policy.h>
François Gaffiec005e562018-11-06 15:04:49 +010033#include <AudioIODescriptorInterface.h>
François Gaffie20f06f92015-03-24 09:01:14 +010034#include <ParameterManagerWrapper.h>
jiabin12dc6b02019-10-01 09:38:30 -070035#include <media/AudioContainers.h>
François Gaffie20f06f92015-03-24 09:01:14 +010036
François Gaffiec60c3692019-08-09 15:41:24 +020037#include <media/TypeConverter.h>
38
Francois Gaffiea12de212021-10-22 10:54:33 +020039#include <cinttypes>
40
François Gaffie20f06f92015-03-24 09:01:14 +010041using std::string;
42using std::map;
43
François Gaffief19cf792018-05-30 17:22:17 +020044namespace android {
45namespace audio_policy {
46
François Gaffie20f06f92015-03-24 09:01:14 +010047template <>
François Gaffie20f06f92015-03-24 09:01:14 +010048StreamCollection &Engine::getCollection<audio_stream_type_t>()
49{
50 return mStreamCollection;
51}
52template <>
François Gaffie20f06f92015-03-24 09:01:14 +010053InputSourceCollection &Engine::getCollection<audio_source_t>()
54{
55 return mInputSourceCollection;
56}
57
58template <>
François Gaffie20f06f92015-03-24 09:01:14 +010059const StreamCollection &Engine::getCollection<audio_stream_type_t>() const
60{
61 return mStreamCollection;
62}
63template <>
François Gaffie20f06f92015-03-24 09:01:14 +010064const InputSourceCollection &Engine::getCollection<audio_source_t>() const
65{
66 return mInputSourceCollection;
67}
68
François Gaffiedc7553f2018-11-02 10:39:57 +010069Engine::Engine() : mPolicyParameterMgr(new ParameterManagerWrapper())
François Gaffie20f06f92015-03-24 09:01:14 +010070{
Mikhail Naganovabb04782023-05-02 13:56:01 -070071}
72
Mikhail Naganov9e459d72023-05-05 17:36:39 -070073status_t Engine::loadFromHalConfigWithFallback(
74 const media::audio::common::AudioHalEngineConfig& config __unused) {
75 // b/242678729. Need to implement for the configurable engine.
76 return INVALID_OPERATION;
77}
78
Mikhail Naganovabb04782023-05-02 13:56:01 -070079status_t Engine::loadFromXmlConfigWithFallback(const std::string& xmlFilePath)
80{
81 status_t loadResult = loadAudioPolicyEngineConfig(xmlFilePath);
François Gaffiedc7553f2018-11-02 10:39:57 +010082 if (loadResult < 0) {
83 ALOGE("Policy Engine configuration is invalid.");
84 }
Mikhail Naganovabb04782023-05-02 13:56:01 -070085 return loadResult;
François Gaffie20f06f92015-03-24 09:01:14 +010086}
87
François Gaffie20f06f92015-03-24 09:01:14 +010088status_t Engine::initCheck()
89{
François Gaffieab1837a2019-10-15 10:48:50 +020090 std::string error;
91 if (mPolicyParameterMgr == nullptr || mPolicyParameterMgr->start(error) != NO_ERROR) {
92 ALOGE("%s: could not start Policy PFW: %s", __FUNCTION__, error.c_str());
François Gaffie0f17ab72015-05-13 18:13:00 +020093 return NO_INIT;
94 }
François Gaffiedc7553f2018-11-02 10:39:57 +010095 return EngineBase::initCheck();
François Gaffie20f06f92015-03-24 09:01:14 +010096}
97
François Gaffie20f06f92015-03-24 09:01:14 +010098template <typename Key>
99Element<Key> *Engine::getFromCollection(const Key &key) const
100{
François Gaffieee486792023-05-05 13:41:49 +0200101 const Collection<Key> &collection = getCollection<Key>();
François Gaffie20f06f92015-03-24 09:01:14 +0100102 return collection.get(key);
103}
104
105template <typename Key>
106status_t Engine::add(const std::string &name, const Key &key)
107{
108 Collection<Key> &collection = getCollection<Key>();
109 return collection.add(name, key);
110}
111
François Gaffie20f06f92015-03-24 09:01:14 +0100112template <typename Property, typename Key>
113Property Engine::getPropertyForKey(Key key) const
114{
115 Element<Key> *element = getFromCollection<Key>(key);
116 if (element == NULL) {
117 ALOGE("%s: Element not found within collection", __FUNCTION__);
118 return static_cast<Property>(0);
119 }
120 return element->template get<Property>();
121}
122
François Gaffiedc7553f2018-11-02 10:39:57 +0100123bool Engine::setVolumeProfileForStream(const audio_stream_type_t &stream,
124 const audio_stream_type_t &profile)
François Gaffied1ab2bd2015-12-02 18:20:06 +0100125{
François Gaffiedc7553f2018-11-02 10:39:57 +0100126 if (setPropertyForKey<audio_stream_type_t, audio_stream_type_t>(stream, profile)) {
Eric Laurentf5aa58d2019-02-22 18:20:11 -0800127 switchVolumeCurve(profile, stream);
François Gaffied1ab2bd2015-12-02 18:20:06 +0100128 return true;
129 }
130 return false;
131}
132
François Gaffie20f06f92015-03-24 09:01:14 +0100133template <typename Property, typename Key>
134bool Engine::setPropertyForKey(const Property &property, const Key &key)
135{
136 Element<Key> *element = getFromCollection<Key>(key);
137 if (element == NULL) {
138 ALOGE("%s: Element not found within collection", __FUNCTION__);
Francois Gaffie15811d32020-01-14 17:26:02 +0100139 return false;
François Gaffie20f06f92015-03-24 09:01:14 +0100140 }
141 return element->template set<Property>(property) == NO_ERROR;
142}
143
François Gaffie20f06f92015-03-24 09:01:14 +0100144status_t Engine::setPhoneState(audio_mode_t mode)
145{
François Gaffiedc7553f2018-11-02 10:39:57 +0100146 status_t status = mPolicyParameterMgr->setPhoneState(mode);
147 if (status != NO_ERROR) {
148 return status;
149 }
150 return EngineBase::setPhoneState(mode);
François Gaffie20f06f92015-03-24 09:01:14 +0100151}
152
153audio_mode_t Engine::getPhoneState() const
154{
155 return mPolicyParameterMgr->getPhoneState();
156}
157
158status_t Engine::setForceUse(audio_policy_force_use_t usage,
159 audio_policy_forced_cfg_t config)
160{
François Gaffiedc7553f2018-11-02 10:39:57 +0100161 status_t status = mPolicyParameterMgr->setForceUse(usage, config);
162 if (status != NO_ERROR) {
163 return status;
164 }
165 return EngineBase::setForceUse(usage, config);
François Gaffie20f06f92015-03-24 09:01:14 +0100166}
167
168audio_policy_forced_cfg_t Engine::getForceUse(audio_policy_force_use_t usage) const
169{
170 return mPolicyParameterMgr->getForceUse(usage);
171}
172
François Gaffieab1837a2019-10-15 10:48:50 +0200173status_t Engine::setDeviceConnectionState(const sp<DeviceDescriptor> device,
François Gaffie3305c112018-02-22 10:56:49 +0100174 audio_policy_dev_state_t state)
François Gaffie20f06f92015-03-24 09:01:14 +0100175{
Francois Gaffiea12de212021-10-22 10:54:33 +0200176 mPolicyParameterMgr->setDeviceConnectionState(device->type(), device->address(), state);
François Gaffieab1837a2019-10-15 10:48:50 +0200177 if (audio_is_output_device(device->type())) {
François Gaffiea3e696d2015-12-18 09:38:43 +0100178 return mPolicyParameterMgr->setAvailableOutputDevices(
Francois Gaffiea12de212021-10-22 10:54:33 +0200179 getApmObserver()->getAvailableOutputDevices().types());
François Gaffieab1837a2019-10-15 10:48:50 +0200180 } else if (audio_is_input_device(device->type())) {
François Gaffiea3e696d2015-12-18 09:38:43 +0100181 return mPolicyParameterMgr->setAvailableInputDevices(
Francois Gaffiea12de212021-10-22 10:54:33 +0200182 getApmObserver()->getAvailableInputDevices().types());
François Gaffiea3e696d2015-12-18 09:38:43 +0100183 }
François Gaffieab1837a2019-10-15 10:48:50 +0200184 return EngineBase::setDeviceConnectionState(device, state);
François Gaffie20f06f92015-03-24 09:01:14 +0100185}
186
Mikhail Naganovabb04782023-05-02 13:56:01 -0700187status_t Engine::loadAudioPolicyEngineConfig(const std::string& xmlFilePath)
François Gaffiedc7553f2018-11-02 10:39:57 +0100188{
Mikhail Naganovabb04782023-05-02 13:56:01 -0700189 auto result = EngineBase::loadAudioPolicyEngineConfig(xmlFilePath);
François Gaffiedc7553f2018-11-02 10:39:57 +0100190
François Gaffie7188f1a2018-11-02 14:35:42 +0100191 // Custom XML Parsing
192 auto loadCriteria= [this](const auto& configCriteria, const auto& configCriterionTypes) {
193 for (auto& criterion : configCriteria) {
194 engineConfig::CriterionType criterionType;
195 for (auto &configCriterionType : configCriterionTypes) {
196 if (configCriterionType.name == criterion.typeName) {
197 criterionType = configCriterionType;
198 break;
199 }
200 }
201 ALOG_ASSERT(not criterionType.name.empty(), "Invalid criterion type for %s",
202 criterion.name.c_str());
203 mPolicyParameterMgr->addCriterion(criterion.name, criterionType.isInclusive,
204 criterionType.valuePairs,
205 criterion.defaultLiteralValue);
206 }
207 };
208
209 loadCriteria(result.parsedConfig->criteria, result.parsedConfig->criterionTypes);
François Gaffiedc7553f2018-11-02 10:39:57 +0100210 return result.nbSkippedElement == 0? NO_ERROR : BAD_VALUE;
211}
212
213DeviceVector Engine::getDevicesForProductStrategy(product_strategy_t ps) const
214{
215 const auto productStrategies = getProductStrategies();
216 if (productStrategies.find(ps) == productStrategies.end()) {
217 ALOGE("%s: Trying to get device on invalid strategy %d", __FUNCTION__, ps);
218 return {};
219 }
Eric Laurentaf377772019-03-29 14:50:21 -0700220 const DeviceVector availableOutputDevices = getApmObserver()->getAvailableOutputDevices();
François Gaffiedc7553f2018-11-02 10:39:57 +0100221 const SwAudioOutputCollection &outputs = getApmObserver()->getOutputs();
jiabin12dc6b02019-10-01 09:38:30 -0700222 DeviceTypeSet availableOutputDevicesTypes = availableOutputDevices.types();
François Gaffiedc7553f2018-11-02 10:39:57 +0100223
224 /** This is the only case handled programmatically because the PFW is unable to know the
225 * activity of streams.
226 *
227 * -While media is playing on a remote device, use the the sonification behavior.
228 * Note that we test this usecase before testing if media is playing because
229 * the isStreamActive() method only informs about the activity of a stream, not
230 * if it's for local playback. Note also that we use the same delay between both tests
231 *
232 * -When media is not playing anymore, fall back on the sonification behavior
233 */
jiabin12dc6b02019-10-01 09:38:30 -0700234 DeviceTypeSet deviceTypes;
François Gaffiedc7553f2018-11-02 10:39:57 +0100235 if (ps == getProductStrategyForStream(AUDIO_STREAM_NOTIFICATION) &&
236 !is_state_in_call(getPhoneState()) &&
Eric Laurent83d17c22019-04-02 17:10:01 -0700237 !outputs.isActiveRemotely(toVolumeSource(AUDIO_STREAM_MUSIC),
François Gaffie1c878552018-11-22 16:53:21 +0100238 SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY) &&
Eric Laurent83d17c22019-04-02 17:10:01 -0700239 outputs.isActive(toVolumeSource(AUDIO_STREAM_MUSIC),
François Gaffie1c878552018-11-22 16:53:21 +0100240 SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY)) {
François Gaffiedc7553f2018-11-02 10:39:57 +0100241 product_strategy_t strategyForMedia =
242 getProductStrategyForStream(AUDIO_STREAM_MUSIC);
jiabin12dc6b02019-10-01 09:38:30 -0700243 deviceTypes = productStrategies.getDeviceTypesForProductStrategy(strategyForMedia);
François Gaffiedc7553f2018-11-02 10:39:57 +0100244 } else if (ps == getProductStrategyForStream(AUDIO_STREAM_ACCESSIBILITY) &&
Eric Laurent83d17c22019-04-02 17:10:01 -0700245 (outputs.isActive(toVolumeSource(AUDIO_STREAM_RING)) ||
246 outputs.isActive(toVolumeSource(AUDIO_STREAM_ALARM)))) {
François Gaffiedc7553f2018-11-02 10:39:57 +0100247 // do not route accessibility prompts to a digital output currently configured with a
248 // compressed format as they would likely not be mixed and dropped.
249 // Device For Sonification conf file has HDMI, SPDIF and HDMI ARC unreacheable.
250 product_strategy_t strategyNotification = getProductStrategyForStream(AUDIO_STREAM_RING);
jiabin12dc6b02019-10-01 09:38:30 -0700251 deviceTypes = productStrategies.getDeviceTypesForProductStrategy(strategyNotification);
François Gaffiedc7553f2018-11-02 10:39:57 +0100252 } else {
jiabin12dc6b02019-10-01 09:38:30 -0700253 deviceTypes = productStrategies.getDeviceTypesForProductStrategy(ps);
François Gaffiedc7553f2018-11-02 10:39:57 +0100254 }
jiabin12dc6b02019-10-01 09:38:30 -0700255 if (deviceTypes.empty() ||
256 Intersection(deviceTypes, availableOutputDevicesTypes).empty()) {
François Gaffiec60c3692019-08-09 15:41:24 +0200257 auto defaultDevice = getApmObserver()->getDefaultOutputDevice();
258 ALOG_ASSERT(defaultDevice != nullptr, "no valid default device defined");
259 return DeviceVector(defaultDevice);
François Gaffiedc7553f2018-11-02 10:39:57 +0100260 }
jiabin12dc6b02019-10-01 09:38:30 -0700261 if (/*device_distinguishes_on_address(*deviceTypes.begin())*/ isSingleDeviceType(
262 deviceTypes, AUDIO_DEVICE_OUT_BUS)) {
François Gaffiedc7553f2018-11-02 10:39:57 +0100263 // We do expect only one device for these types of devices
264 // Criterion device address garantee this one is available
265 // If this criterion is not wished, need to ensure this device is available
266 const String8 address(productStrategies.getDeviceAddressForProductStrategy(ps).c_str());
jiabin12dc6b02019-10-01 09:38:30 -0700267 ALOGV("%s:device %s %s %d",
268 __FUNCTION__, dumpDeviceTypes(deviceTypes).c_str(), address.c_str(), ps);
269 auto busDevice = availableOutputDevices.getDevice(
270 *deviceTypes.begin(), address, AUDIO_FORMAT_DEFAULT);
François Gaffiec60c3692019-08-09 15:41:24 +0200271 if (busDevice == nullptr) {
jiabin12dc6b02019-10-01 09:38:30 -0700272 ALOGE("%s:unavailable device %s %s, fallback on default", __func__,
273 dumpDeviceTypes(deviceTypes).c_str(), address.c_str());
François Gaffiec60c3692019-08-09 15:41:24 +0200274 auto defaultDevice = getApmObserver()->getDefaultOutputDevice();
275 ALOG_ASSERT(defaultDevice != nullptr, "Default Output Device NOT available");
276 return DeviceVector(defaultDevice);
277 }
jiabin12dc6b02019-10-01 09:38:30 -0700278 return DeviceVector(busDevice);
François Gaffiedc7553f2018-11-02 10:39:57 +0100279 }
jiabin12dc6b02019-10-01 09:38:30 -0700280 ALOGV("%s:device %s %d", __FUNCTION__, dumpDeviceTypes(deviceTypes).c_str(), ps);
281 return availableOutputDevices.getDevicesFromTypes(deviceTypes);
François Gaffiedc7553f2018-11-02 10:39:57 +0100282}
283
284DeviceVector Engine::getOutputDevicesForAttributes(const audio_attributes_t &attributes,
285 const sp<DeviceDescriptor> &preferredDevice,
286 bool fromCache) const
287{
288 // First check for explict routing device
289 if (preferredDevice != nullptr) {
290 ALOGV("%s explicit Routing on device %s", __func__, preferredDevice->toString().c_str());
291 return DeviceVector(preferredDevice);
292 }
François Gaffiec005e562018-11-06 15:04:49 +0100293 product_strategy_t strategy = getProductStrategyForAttributes(attributes);
Eric Laurentaf377772019-03-29 14:50:21 -0700294 const DeviceVector availableOutputDevices = getApmObserver()->getAvailableOutputDevices();
François Gaffiec005e562018-11-06 15:04:49 +0100295 const SwAudioOutputCollection &outputs = getApmObserver()->getOutputs();
François Gaffiedc7553f2018-11-02 10:39:57 +0100296 //
François Gaffiec005e562018-11-06 15:04:49 +0100297 // @TODO: what is the priority of explicit routing? Shall it be considered first as it used to
298 // be by APM?
François Gaffiedc7553f2018-11-02 10:39:57 +0100299 //
François Gaffiec005e562018-11-06 15:04:49 +0100300 // Honor explicit routing requests only if all active clients have a preferred route in which
301 // case the last active client route is used
302 sp<DeviceDescriptor> device = findPreferredDevice(outputs, strategy, availableOutputDevices);
303 if (device != nullptr) {
304 return DeviceVector(device);
305 }
Francois Gaffie6f52ff92020-08-25 08:53:53 +0200306 return fromCache? getCachedDevices(strategy) : getDevicesForProductStrategy(strategy);
307}
François Gaffiec005e562018-11-06 15:04:49 +0100308
Francois Gaffie6f52ff92020-08-25 08:53:53 +0200309DeviceVector Engine::getCachedDevices(product_strategy_t ps) const
310{
311 return mDevicesForStrategies.find(ps) != mDevicesForStrategies.end() ?
312 mDevicesForStrategies.at(ps) : DeviceVector{};
François Gaffiedc7553f2018-11-02 10:39:57 +0100313}
314
315DeviceVector Engine::getOutputDevicesForStream(audio_stream_type_t stream, bool fromCache) const
316{
317 auto attributes = EngineBase::getAttributesForStreamType(stream);
318 return getOutputDevicesForAttributes(attributes, nullptr, fromCache);
319}
320
321sp<DeviceDescriptor> Engine::getInputDeviceForAttributes(const audio_attributes_t &attr,
yuanjiahsu0735bf32021-03-18 08:12:54 +0800322 uid_t uid,
Mikhail Naganovbfac5832019-03-05 16:55:28 -0800323 sp<AudioPolicyMix> *mix) const
François Gaffiedc7553f2018-11-02 10:39:57 +0100324{
François Gaffiec005e562018-11-06 15:04:49 +0100325 const auto &policyMixes = getApmObserver()->getAudioPolicyMixCollection();
Eric Laurentaf377772019-03-29 14:50:21 -0700326 const auto availableInputDevices = getApmObserver()->getAvailableInputDevices();
François Gaffiec005e562018-11-06 15:04:49 +0100327 const auto &inputs = getApmObserver()->getInputs();
François Gaffiedc7553f2018-11-02 10:39:57 +0100328 std::string address;
329 //
François Gaffiec005e562018-11-06 15:04:49 +0100330 // Explicit Routing ??? what is the priority of explicit routing? Shall it be considered
331 // first as it used to be by APM?
François Gaffiedc7553f2018-11-02 10:39:57 +0100332 //
François Gaffiec005e562018-11-06 15:04:49 +0100333 // Honor explicit routing requests only if all active clients have a preferred route in which
334 // case the last active client route is used
335 sp<DeviceDescriptor> device =
336 findPreferredDevice(inputs, attr.source, availableInputDevices);
337 if (device != nullptr) {
338 return device;
339 }
340
Jan Sebechlebskya7ed5272022-09-15 17:57:42 +0200341 device = policyMixes.getDeviceAndMixForInputSource(attr,
yuanjiahsu0735bf32021-03-18 08:12:54 +0800342 availableInputDevices,
343 uid,
344 mix);
François Gaffiec005e562018-11-06 15:04:49 +0100345 if (device != nullptr) {
346 return device;
347 }
348
François Gaffiedc7553f2018-11-02 10:39:57 +0100349 audio_devices_t deviceType = getPropertyForKey<audio_devices_t, audio_source_t>(attr.source);
350
351 if (audio_is_remote_submix_device(deviceType)) {
352 address = "0";
353 std::size_t pos;
354 std::string tags { attr.tags };
355 if ((pos = tags.find("addr=")) != std::string::npos) {
356 address = tags.substr(pos + std::strlen("addr="));
357 }
358 }
François Gaffiec005e562018-11-06 15:04:49 +0100359 return availableInputDevices.getDevice(deviceType, String8(address.c_str()), AUDIO_FORMAT_DEFAULT);
François Gaffiedc7553f2018-11-02 10:39:57 +0100360}
361
François Gaffief1e95082018-11-02 13:53:31 +0100362void Engine::setDeviceAddressForProductStrategy(product_strategy_t strategy,
363 const std::string &address)
364{
365 if (getProductStrategies().find(strategy) == getProductStrategies().end()) {
366 ALOGE("%s: Trying to set address %s on invalid strategy %d", __FUNCTION__, address.c_str(),
367 strategy);
368 return;
369 }
370 getProductStrategies().at(strategy)->setDeviceAddress(address);
371}
372
Francois Gaffiea12de212021-10-22 10:54:33 +0200373bool Engine::setDeviceTypesForProductStrategy(product_strategy_t strategy, uint64_t devices)
François Gaffief1e95082018-11-02 13:53:31 +0100374{
375 if (getProductStrategies().find(strategy) == getProductStrategies().end()) {
Francois Gaffiea12de212021-10-22 10:54:33 +0200376 ALOGE("%s: set device %" PRId64 " on invalid strategy %d", __FUNCTION__, devices, strategy);
François Gaffief1e95082018-11-02 13:53:31 +0100377 return false;
378 }
Francois Gaffiea12de212021-10-22 10:54:33 +0200379 // Here device matches the criterion value, need to rebuitd android device types;
380 DeviceTypeSet types =
381 mPolicyParameterMgr->convertDeviceCriterionValueToDeviceTypes(devices, true /*isOut*/);
382 getProductStrategies().at(strategy)->setDeviceTypes(types);
François Gaffief1e95082018-11-02 13:53:31 +0100383 return true;
384}
385
Francois Gaffiea12de212021-10-22 10:54:33 +0200386bool Engine::setDeviceForInputSource(const audio_source_t &inputSource, uint64_t device)
387{
388 DeviceTypeSet types = mPolicyParameterMgr->convertDeviceCriterionValueToDeviceTypes(
389 device, false /*isOut*/);
390 ALOG_ASSERT(types.size() <= 1, "one input device expected at most");
391 audio_devices_t deviceType = types.empty() ? AUDIO_DEVICE_IN_DEFAULT : *types.begin();
392 return setPropertyForKey<audio_devices_t, audio_source_t>(deviceType, inputSource);
393}
394
François Gaffie20f06f92015-03-24 09:01:14 +0100395template <>
Mikhail Naganove13c6792019-05-14 10:32:51 -0700396EngineInterface *Engine::queryInterface()
François Gaffie20f06f92015-03-24 09:01:14 +0100397{
François Gaffiedc7553f2018-11-02 10:39:57 +0100398 return this;
François Gaffie20f06f92015-03-24 09:01:14 +0100399}
400
401template <>
402AudioPolicyPluginInterface *Engine::queryInterface()
403{
François Gaffiedc7553f2018-11-02 10:39:57 +0100404 return this;
François Gaffie20f06f92015-03-24 09:01:14 +0100405}
406
407} // namespace audio_policy
408} // namespace android