blob: 44f1d7cfb3de68511dc1458517b34ddcc9584d4f [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
73status_t Engine::loadFromXmlConfigWithFallback(const std::string& xmlFilePath)
74{
75 status_t loadResult = loadAudioPolicyEngineConfig(xmlFilePath);
François Gaffiedc7553f2018-11-02 10:39:57 +010076 if (loadResult < 0) {
77 ALOGE("Policy Engine configuration is invalid.");
78 }
Mikhail Naganovabb04782023-05-02 13:56:01 -070079 return loadResult;
François Gaffie20f06f92015-03-24 09:01:14 +010080}
81
François Gaffie20f06f92015-03-24 09:01:14 +010082status_t Engine::initCheck()
83{
François Gaffieab1837a2019-10-15 10:48:50 +020084 std::string error;
85 if (mPolicyParameterMgr == nullptr || mPolicyParameterMgr->start(error) != NO_ERROR) {
86 ALOGE("%s: could not start Policy PFW: %s", __FUNCTION__, error.c_str());
François Gaffie0f17ab72015-05-13 18:13:00 +020087 return NO_INIT;
88 }
François Gaffiedc7553f2018-11-02 10:39:57 +010089 return EngineBase::initCheck();
François Gaffie20f06f92015-03-24 09:01:14 +010090}
91
François Gaffie20f06f92015-03-24 09:01:14 +010092template <typename Key>
93Element<Key> *Engine::getFromCollection(const Key &key) const
94{
François Gaffieee486792023-05-05 13:41:49 +020095 const Collection<Key> &collection = getCollection<Key>();
François Gaffie20f06f92015-03-24 09:01:14 +010096 return collection.get(key);
97}
98
99template <typename Key>
100status_t Engine::add(const std::string &name, const Key &key)
101{
102 Collection<Key> &collection = getCollection<Key>();
103 return collection.add(name, key);
104}
105
François Gaffie20f06f92015-03-24 09:01:14 +0100106template <typename Property, typename Key>
107Property Engine::getPropertyForKey(Key key) const
108{
109 Element<Key> *element = getFromCollection<Key>(key);
110 if (element == NULL) {
111 ALOGE("%s: Element not found within collection", __FUNCTION__);
112 return static_cast<Property>(0);
113 }
114 return element->template get<Property>();
115}
116
François Gaffiedc7553f2018-11-02 10:39:57 +0100117bool Engine::setVolumeProfileForStream(const audio_stream_type_t &stream,
118 const audio_stream_type_t &profile)
François Gaffied1ab2bd2015-12-02 18:20:06 +0100119{
François Gaffiedc7553f2018-11-02 10:39:57 +0100120 if (setPropertyForKey<audio_stream_type_t, audio_stream_type_t>(stream, profile)) {
Eric Laurentf5aa58d2019-02-22 18:20:11 -0800121 switchVolumeCurve(profile, stream);
François Gaffied1ab2bd2015-12-02 18:20:06 +0100122 return true;
123 }
124 return false;
125}
126
François Gaffie20f06f92015-03-24 09:01:14 +0100127template <typename Property, typename Key>
128bool Engine::setPropertyForKey(const Property &property, const Key &key)
129{
130 Element<Key> *element = getFromCollection<Key>(key);
131 if (element == NULL) {
132 ALOGE("%s: Element not found within collection", __FUNCTION__);
Francois Gaffie15811d32020-01-14 17:26:02 +0100133 return false;
François Gaffie20f06f92015-03-24 09:01:14 +0100134 }
135 return element->template set<Property>(property) == NO_ERROR;
136}
137
François Gaffie20f06f92015-03-24 09:01:14 +0100138status_t Engine::setPhoneState(audio_mode_t mode)
139{
François Gaffiedc7553f2018-11-02 10:39:57 +0100140 status_t status = mPolicyParameterMgr->setPhoneState(mode);
141 if (status != NO_ERROR) {
142 return status;
143 }
144 return EngineBase::setPhoneState(mode);
François Gaffie20f06f92015-03-24 09:01:14 +0100145}
146
147audio_mode_t Engine::getPhoneState() const
148{
149 return mPolicyParameterMgr->getPhoneState();
150}
151
152status_t Engine::setForceUse(audio_policy_force_use_t usage,
153 audio_policy_forced_cfg_t config)
154{
François Gaffiedc7553f2018-11-02 10:39:57 +0100155 status_t status = mPolicyParameterMgr->setForceUse(usage, config);
156 if (status != NO_ERROR) {
157 return status;
158 }
159 return EngineBase::setForceUse(usage, config);
François Gaffie20f06f92015-03-24 09:01:14 +0100160}
161
162audio_policy_forced_cfg_t Engine::getForceUse(audio_policy_force_use_t usage) const
163{
164 return mPolicyParameterMgr->getForceUse(usage);
165}
166
François Gaffieab1837a2019-10-15 10:48:50 +0200167status_t Engine::setDeviceConnectionState(const sp<DeviceDescriptor> device,
François Gaffie3305c112018-02-22 10:56:49 +0100168 audio_policy_dev_state_t state)
François Gaffie20f06f92015-03-24 09:01:14 +0100169{
Francois Gaffiea12de212021-10-22 10:54:33 +0200170 mPolicyParameterMgr->setDeviceConnectionState(device->type(), device->address(), state);
François Gaffieab1837a2019-10-15 10:48:50 +0200171 if (audio_is_output_device(device->type())) {
François Gaffiea3e696d2015-12-18 09:38:43 +0100172 return mPolicyParameterMgr->setAvailableOutputDevices(
Francois Gaffiea12de212021-10-22 10:54:33 +0200173 getApmObserver()->getAvailableOutputDevices().types());
François Gaffieab1837a2019-10-15 10:48:50 +0200174 } else if (audio_is_input_device(device->type())) {
François Gaffiea3e696d2015-12-18 09:38:43 +0100175 return mPolicyParameterMgr->setAvailableInputDevices(
Francois Gaffiea12de212021-10-22 10:54:33 +0200176 getApmObserver()->getAvailableInputDevices().types());
François Gaffiea3e696d2015-12-18 09:38:43 +0100177 }
François Gaffieab1837a2019-10-15 10:48:50 +0200178 return EngineBase::setDeviceConnectionState(device, state);
François Gaffie20f06f92015-03-24 09:01:14 +0100179}
180
Mikhail Naganovabb04782023-05-02 13:56:01 -0700181status_t Engine::loadAudioPolicyEngineConfig(const std::string& xmlFilePath)
François Gaffiedc7553f2018-11-02 10:39:57 +0100182{
Mikhail Naganovabb04782023-05-02 13:56:01 -0700183 auto result = EngineBase::loadAudioPolicyEngineConfig(xmlFilePath);
François Gaffiedc7553f2018-11-02 10:39:57 +0100184
François Gaffie7188f1a2018-11-02 14:35:42 +0100185 // Custom XML Parsing
186 auto loadCriteria= [this](const auto& configCriteria, const auto& configCriterionTypes) {
187 for (auto& criterion : configCriteria) {
188 engineConfig::CriterionType criterionType;
189 for (auto &configCriterionType : configCriterionTypes) {
190 if (configCriterionType.name == criterion.typeName) {
191 criterionType = configCriterionType;
192 break;
193 }
194 }
195 ALOG_ASSERT(not criterionType.name.empty(), "Invalid criterion type for %s",
196 criterion.name.c_str());
197 mPolicyParameterMgr->addCriterion(criterion.name, criterionType.isInclusive,
198 criterionType.valuePairs,
199 criterion.defaultLiteralValue);
200 }
201 };
202
203 loadCriteria(result.parsedConfig->criteria, result.parsedConfig->criterionTypes);
François Gaffiedc7553f2018-11-02 10:39:57 +0100204 return result.nbSkippedElement == 0? NO_ERROR : BAD_VALUE;
205}
206
207DeviceVector Engine::getDevicesForProductStrategy(product_strategy_t ps) const
208{
209 const auto productStrategies = getProductStrategies();
210 if (productStrategies.find(ps) == productStrategies.end()) {
211 ALOGE("%s: Trying to get device on invalid strategy %d", __FUNCTION__, ps);
212 return {};
213 }
Eric Laurentaf377772019-03-29 14:50:21 -0700214 const DeviceVector availableOutputDevices = getApmObserver()->getAvailableOutputDevices();
François Gaffiedc7553f2018-11-02 10:39:57 +0100215 const SwAudioOutputCollection &outputs = getApmObserver()->getOutputs();
jiabin12dc6b02019-10-01 09:38:30 -0700216 DeviceTypeSet availableOutputDevicesTypes = availableOutputDevices.types();
François Gaffiedc7553f2018-11-02 10:39:57 +0100217
218 /** This is the only case handled programmatically because the PFW is unable to know the
219 * activity of streams.
220 *
221 * -While media is playing on a remote device, use the the sonification behavior.
222 * Note that we test this usecase before testing if media is playing because
223 * the isStreamActive() method only informs about the activity of a stream, not
224 * if it's for local playback. Note also that we use the same delay between both tests
225 *
226 * -When media is not playing anymore, fall back on the sonification behavior
227 */
jiabin12dc6b02019-10-01 09:38:30 -0700228 DeviceTypeSet deviceTypes;
François Gaffiedc7553f2018-11-02 10:39:57 +0100229 if (ps == getProductStrategyForStream(AUDIO_STREAM_NOTIFICATION) &&
230 !is_state_in_call(getPhoneState()) &&
Eric Laurent83d17c22019-04-02 17:10:01 -0700231 !outputs.isActiveRemotely(toVolumeSource(AUDIO_STREAM_MUSIC),
François Gaffie1c878552018-11-22 16:53:21 +0100232 SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY) &&
Eric Laurent83d17c22019-04-02 17:10:01 -0700233 outputs.isActive(toVolumeSource(AUDIO_STREAM_MUSIC),
François Gaffie1c878552018-11-22 16:53:21 +0100234 SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY)) {
François Gaffiedc7553f2018-11-02 10:39:57 +0100235 product_strategy_t strategyForMedia =
236 getProductStrategyForStream(AUDIO_STREAM_MUSIC);
jiabin12dc6b02019-10-01 09:38:30 -0700237 deviceTypes = productStrategies.getDeviceTypesForProductStrategy(strategyForMedia);
François Gaffiedc7553f2018-11-02 10:39:57 +0100238 } else if (ps == getProductStrategyForStream(AUDIO_STREAM_ACCESSIBILITY) &&
Eric Laurent83d17c22019-04-02 17:10:01 -0700239 (outputs.isActive(toVolumeSource(AUDIO_STREAM_RING)) ||
240 outputs.isActive(toVolumeSource(AUDIO_STREAM_ALARM)))) {
François Gaffiedc7553f2018-11-02 10:39:57 +0100241 // do not route accessibility prompts to a digital output currently configured with a
242 // compressed format as they would likely not be mixed and dropped.
243 // Device For Sonification conf file has HDMI, SPDIF and HDMI ARC unreacheable.
244 product_strategy_t strategyNotification = getProductStrategyForStream(AUDIO_STREAM_RING);
jiabin12dc6b02019-10-01 09:38:30 -0700245 deviceTypes = productStrategies.getDeviceTypesForProductStrategy(strategyNotification);
François Gaffiedc7553f2018-11-02 10:39:57 +0100246 } else {
jiabin12dc6b02019-10-01 09:38:30 -0700247 deviceTypes = productStrategies.getDeviceTypesForProductStrategy(ps);
François Gaffiedc7553f2018-11-02 10:39:57 +0100248 }
jiabin12dc6b02019-10-01 09:38:30 -0700249 if (deviceTypes.empty() ||
250 Intersection(deviceTypes, availableOutputDevicesTypes).empty()) {
François Gaffiec60c3692019-08-09 15:41:24 +0200251 auto defaultDevice = getApmObserver()->getDefaultOutputDevice();
252 ALOG_ASSERT(defaultDevice != nullptr, "no valid default device defined");
253 return DeviceVector(defaultDevice);
François Gaffiedc7553f2018-11-02 10:39:57 +0100254 }
jiabin12dc6b02019-10-01 09:38:30 -0700255 if (/*device_distinguishes_on_address(*deviceTypes.begin())*/ isSingleDeviceType(
256 deviceTypes, AUDIO_DEVICE_OUT_BUS)) {
François Gaffiedc7553f2018-11-02 10:39:57 +0100257 // We do expect only one device for these types of devices
258 // Criterion device address garantee this one is available
259 // If this criterion is not wished, need to ensure this device is available
260 const String8 address(productStrategies.getDeviceAddressForProductStrategy(ps).c_str());
jiabin12dc6b02019-10-01 09:38:30 -0700261 ALOGV("%s:device %s %s %d",
262 __FUNCTION__, dumpDeviceTypes(deviceTypes).c_str(), address.c_str(), ps);
263 auto busDevice = availableOutputDevices.getDevice(
264 *deviceTypes.begin(), address, AUDIO_FORMAT_DEFAULT);
François Gaffiec60c3692019-08-09 15:41:24 +0200265 if (busDevice == nullptr) {
jiabin12dc6b02019-10-01 09:38:30 -0700266 ALOGE("%s:unavailable device %s %s, fallback on default", __func__,
267 dumpDeviceTypes(deviceTypes).c_str(), address.c_str());
François Gaffiec60c3692019-08-09 15:41:24 +0200268 auto defaultDevice = getApmObserver()->getDefaultOutputDevice();
269 ALOG_ASSERT(defaultDevice != nullptr, "Default Output Device NOT available");
270 return DeviceVector(defaultDevice);
271 }
jiabin12dc6b02019-10-01 09:38:30 -0700272 return DeviceVector(busDevice);
François Gaffiedc7553f2018-11-02 10:39:57 +0100273 }
jiabin12dc6b02019-10-01 09:38:30 -0700274 ALOGV("%s:device %s %d", __FUNCTION__, dumpDeviceTypes(deviceTypes).c_str(), ps);
275 return availableOutputDevices.getDevicesFromTypes(deviceTypes);
François Gaffiedc7553f2018-11-02 10:39:57 +0100276}
277
278DeviceVector Engine::getOutputDevicesForAttributes(const audio_attributes_t &attributes,
279 const sp<DeviceDescriptor> &preferredDevice,
280 bool fromCache) const
281{
282 // First check for explict routing device
283 if (preferredDevice != nullptr) {
284 ALOGV("%s explicit Routing on device %s", __func__, preferredDevice->toString().c_str());
285 return DeviceVector(preferredDevice);
286 }
François Gaffiec005e562018-11-06 15:04:49 +0100287 product_strategy_t strategy = getProductStrategyForAttributes(attributes);
Eric Laurentaf377772019-03-29 14:50:21 -0700288 const DeviceVector availableOutputDevices = getApmObserver()->getAvailableOutputDevices();
François Gaffiec005e562018-11-06 15:04:49 +0100289 const SwAudioOutputCollection &outputs = getApmObserver()->getOutputs();
François Gaffiedc7553f2018-11-02 10:39:57 +0100290 //
François Gaffiec005e562018-11-06 15:04:49 +0100291 // @TODO: what is the priority of explicit routing? Shall it be considered first as it used to
292 // be by APM?
François Gaffiedc7553f2018-11-02 10:39:57 +0100293 //
François Gaffiec005e562018-11-06 15:04:49 +0100294 // Honor explicit routing requests only if all active clients have a preferred route in which
295 // case the last active client route is used
296 sp<DeviceDescriptor> device = findPreferredDevice(outputs, strategy, availableOutputDevices);
297 if (device != nullptr) {
298 return DeviceVector(device);
299 }
Francois Gaffie6f52ff92020-08-25 08:53:53 +0200300 return fromCache? getCachedDevices(strategy) : getDevicesForProductStrategy(strategy);
301}
François Gaffiec005e562018-11-06 15:04:49 +0100302
Francois Gaffie6f52ff92020-08-25 08:53:53 +0200303DeviceVector Engine::getCachedDevices(product_strategy_t ps) const
304{
305 return mDevicesForStrategies.find(ps) != mDevicesForStrategies.end() ?
306 mDevicesForStrategies.at(ps) : DeviceVector{};
François Gaffiedc7553f2018-11-02 10:39:57 +0100307}
308
309DeviceVector Engine::getOutputDevicesForStream(audio_stream_type_t stream, bool fromCache) const
310{
311 auto attributes = EngineBase::getAttributesForStreamType(stream);
312 return getOutputDevicesForAttributes(attributes, nullptr, fromCache);
313}
314
315sp<DeviceDescriptor> Engine::getInputDeviceForAttributes(const audio_attributes_t &attr,
yuanjiahsu0735bf32021-03-18 08:12:54 +0800316 uid_t uid,
Mikhail Naganovbfac5832019-03-05 16:55:28 -0800317 sp<AudioPolicyMix> *mix) const
François Gaffiedc7553f2018-11-02 10:39:57 +0100318{
François Gaffiec005e562018-11-06 15:04:49 +0100319 const auto &policyMixes = getApmObserver()->getAudioPolicyMixCollection();
Eric Laurentaf377772019-03-29 14:50:21 -0700320 const auto availableInputDevices = getApmObserver()->getAvailableInputDevices();
François Gaffiec005e562018-11-06 15:04:49 +0100321 const auto &inputs = getApmObserver()->getInputs();
François Gaffiedc7553f2018-11-02 10:39:57 +0100322 std::string address;
323 //
François Gaffiec005e562018-11-06 15:04:49 +0100324 // Explicit Routing ??? what is the priority of explicit routing? Shall it be considered
325 // first as it used to be by APM?
François Gaffiedc7553f2018-11-02 10:39:57 +0100326 //
François Gaffiec005e562018-11-06 15:04:49 +0100327 // Honor explicit routing requests only if all active clients have a preferred route in which
328 // case the last active client route is used
329 sp<DeviceDescriptor> device =
330 findPreferredDevice(inputs, attr.source, availableInputDevices);
331 if (device != nullptr) {
332 return device;
333 }
334
Jan Sebechlebskya7ed5272022-09-15 17:57:42 +0200335 device = policyMixes.getDeviceAndMixForInputSource(attr,
yuanjiahsu0735bf32021-03-18 08:12:54 +0800336 availableInputDevices,
337 uid,
338 mix);
François Gaffiec005e562018-11-06 15:04:49 +0100339 if (device != nullptr) {
340 return device;
341 }
342
François Gaffiedc7553f2018-11-02 10:39:57 +0100343 audio_devices_t deviceType = getPropertyForKey<audio_devices_t, audio_source_t>(attr.source);
344
345 if (audio_is_remote_submix_device(deviceType)) {
346 address = "0";
347 std::size_t pos;
348 std::string tags { attr.tags };
349 if ((pos = tags.find("addr=")) != std::string::npos) {
350 address = tags.substr(pos + std::strlen("addr="));
351 }
352 }
François Gaffiec005e562018-11-06 15:04:49 +0100353 return availableInputDevices.getDevice(deviceType, String8(address.c_str()), AUDIO_FORMAT_DEFAULT);
François Gaffiedc7553f2018-11-02 10:39:57 +0100354}
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
Francois Gaffiea12de212021-10-22 10:54:33 +0200367bool Engine::setDeviceTypesForProductStrategy(product_strategy_t strategy, uint64_t devices)
François Gaffief1e95082018-11-02 13:53:31 +0100368{
369 if (getProductStrategies().find(strategy) == getProductStrategies().end()) {
Francois Gaffiea12de212021-10-22 10:54:33 +0200370 ALOGE("%s: set device %" PRId64 " on invalid strategy %d", __FUNCTION__, devices, strategy);
François Gaffief1e95082018-11-02 13:53:31 +0100371 return false;
372 }
Francois Gaffiea12de212021-10-22 10:54:33 +0200373 // Here device matches the criterion value, need to rebuitd android device types;
374 DeviceTypeSet types =
375 mPolicyParameterMgr->convertDeviceCriterionValueToDeviceTypes(devices, true /*isOut*/);
376 getProductStrategies().at(strategy)->setDeviceTypes(types);
François Gaffief1e95082018-11-02 13:53:31 +0100377 return true;
378}
379
Francois Gaffiea12de212021-10-22 10:54:33 +0200380bool Engine::setDeviceForInputSource(const audio_source_t &inputSource, uint64_t device)
381{
382 DeviceTypeSet types = mPolicyParameterMgr->convertDeviceCriterionValueToDeviceTypes(
383 device, false /*isOut*/);
384 ALOG_ASSERT(types.size() <= 1, "one input device expected at most");
385 audio_devices_t deviceType = types.empty() ? AUDIO_DEVICE_IN_DEFAULT : *types.begin();
386 return setPropertyForKey<audio_devices_t, audio_source_t>(deviceType, inputSource);
387}
388
François Gaffie20f06f92015-03-24 09:01:14 +0100389template <>
Mikhail Naganove13c6792019-05-14 10:32:51 -0700390EngineInterface *Engine::queryInterface()
François Gaffie20f06f92015-03-24 09:01:14 +0100391{
François Gaffiedc7553f2018-11-02 10:39:57 +0100392 return this;
François Gaffie20f06f92015-03-24 09:01:14 +0100393}
394
395template <>
396AudioPolicyPluginInterface *Engine::queryInterface()
397{
François Gaffiedc7553f2018-11-02 10:39:57 +0100398 return this;
François Gaffie20f06f92015-03-24 09:01:14 +0100399}
400
401} // namespace audio_policy
402} // namespace android