blob: 9d53017e51d5c9718381cda6daa738fe0cf7d753 [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{
François Gaffiedc7553f2018-11-02 10:39:57 +010071 status_t loadResult = loadAudioPolicyEngineConfig();
72 if (loadResult < 0) {
73 ALOGE("Policy Engine configuration is invalid.");
74 }
François Gaffie20f06f92015-03-24 09:01:14 +010075}
76
77Engine::~Engine()
78{
François Gaffie20f06f92015-03-24 09:01:14 +010079 mStreamCollection.clear();
80 mInputSourceCollection.clear();
François Gaffie20f06f92015-03-24 09:01:14 +010081}
82
François Gaffie20f06f92015-03-24 09:01:14 +010083status_t Engine::initCheck()
84{
François Gaffieab1837a2019-10-15 10:48:50 +020085 std::string error;
86 if (mPolicyParameterMgr == nullptr || mPolicyParameterMgr->start(error) != NO_ERROR) {
87 ALOGE("%s: could not start Policy PFW: %s", __FUNCTION__, error.c_str());
François Gaffie0f17ab72015-05-13 18:13:00 +020088 return NO_INIT;
89 }
François Gaffiedc7553f2018-11-02 10:39:57 +010090 return EngineBase::initCheck();
François Gaffie20f06f92015-03-24 09:01:14 +010091}
92
François Gaffie20f06f92015-03-24 09:01:14 +010093template <typename Key>
94Element<Key> *Engine::getFromCollection(const Key &key) const
95{
96 const Collection<Key> collection = getCollection<Key>();
97 return collection.get(key);
98}
99
100template <typename Key>
101status_t Engine::add(const std::string &name, const Key &key)
102{
103 Collection<Key> &collection = getCollection<Key>();
104 return collection.add(name, key);
105}
106
François Gaffie20f06f92015-03-24 09:01:14 +0100107template <typename Property, typename Key>
108Property Engine::getPropertyForKey(Key key) const
109{
110 Element<Key> *element = getFromCollection<Key>(key);
111 if (element == NULL) {
112 ALOGE("%s: Element not found within collection", __FUNCTION__);
113 return static_cast<Property>(0);
114 }
115 return element->template get<Property>();
116}
117
François Gaffiedc7553f2018-11-02 10:39:57 +0100118bool Engine::setVolumeProfileForStream(const audio_stream_type_t &stream,
119 const audio_stream_type_t &profile)
François Gaffied1ab2bd2015-12-02 18:20:06 +0100120{
François Gaffiedc7553f2018-11-02 10:39:57 +0100121 if (setPropertyForKey<audio_stream_type_t, audio_stream_type_t>(stream, profile)) {
Eric Laurentf5aa58d2019-02-22 18:20:11 -0800122 switchVolumeCurve(profile, stream);
François Gaffied1ab2bd2015-12-02 18:20:06 +0100123 return true;
124 }
125 return false;
126}
127
François Gaffie20f06f92015-03-24 09:01:14 +0100128template <typename Property, typename Key>
129bool Engine::setPropertyForKey(const Property &property, const Key &key)
130{
131 Element<Key> *element = getFromCollection<Key>(key);
132 if (element == NULL) {
133 ALOGE("%s: Element not found within collection", __FUNCTION__);
Francois Gaffie15811d32020-01-14 17:26:02 +0100134 return false;
François Gaffie20f06f92015-03-24 09:01:14 +0100135 }
136 return element->template set<Property>(property) == NO_ERROR;
137}
138
François Gaffie20f06f92015-03-24 09:01:14 +0100139status_t Engine::setPhoneState(audio_mode_t mode)
140{
François Gaffiedc7553f2018-11-02 10:39:57 +0100141 status_t status = mPolicyParameterMgr->setPhoneState(mode);
142 if (status != NO_ERROR) {
143 return status;
144 }
145 return EngineBase::setPhoneState(mode);
François Gaffie20f06f92015-03-24 09:01:14 +0100146}
147
148audio_mode_t Engine::getPhoneState() const
149{
150 return mPolicyParameterMgr->getPhoneState();
151}
152
153status_t Engine::setForceUse(audio_policy_force_use_t usage,
154 audio_policy_forced_cfg_t config)
155{
François Gaffiedc7553f2018-11-02 10:39:57 +0100156 status_t status = mPolicyParameterMgr->setForceUse(usage, config);
157 if (status != NO_ERROR) {
158 return status;
159 }
160 return EngineBase::setForceUse(usage, config);
François Gaffie20f06f92015-03-24 09:01:14 +0100161}
162
163audio_policy_forced_cfg_t Engine::getForceUse(audio_policy_force_use_t usage) const
164{
165 return mPolicyParameterMgr->getForceUse(usage);
166}
167
François Gaffieab1837a2019-10-15 10:48:50 +0200168status_t Engine::setDeviceConnectionState(const sp<DeviceDescriptor> device,
François Gaffie3305c112018-02-22 10:56:49 +0100169 audio_policy_dev_state_t state)
François Gaffie20f06f92015-03-24 09:01:14 +0100170{
Francois Gaffiea12de212021-10-22 10:54:33 +0200171 mPolicyParameterMgr->setDeviceConnectionState(device->type(), device->address(), state);
François Gaffieab1837a2019-10-15 10:48:50 +0200172 if (audio_is_output_device(device->type())) {
François Gaffiea3e696d2015-12-18 09:38:43 +0100173 return mPolicyParameterMgr->setAvailableOutputDevices(
Francois Gaffiea12de212021-10-22 10:54:33 +0200174 getApmObserver()->getAvailableOutputDevices().types());
François Gaffieab1837a2019-10-15 10:48:50 +0200175 } else if (audio_is_input_device(device->type())) {
François Gaffiea3e696d2015-12-18 09:38:43 +0100176 return mPolicyParameterMgr->setAvailableInputDevices(
Francois Gaffiea12de212021-10-22 10:54:33 +0200177 getApmObserver()->getAvailableInputDevices().types());
François Gaffiea3e696d2015-12-18 09:38:43 +0100178 }
François Gaffieab1837a2019-10-15 10:48:50 +0200179 return EngineBase::setDeviceConnectionState(device, state);
François Gaffie20f06f92015-03-24 09:01:14 +0100180}
181
François Gaffiedc7553f2018-11-02 10:39:57 +0100182status_t Engine::loadAudioPolicyEngineConfig()
183{
184 auto result = EngineBase::loadAudioPolicyEngineConfig();
185
François Gaffie7188f1a2018-11-02 14:35:42 +0100186 // Custom XML Parsing
187 auto loadCriteria= [this](const auto& configCriteria, const auto& configCriterionTypes) {
188 for (auto& criterion : configCriteria) {
189 engineConfig::CriterionType criterionType;
190 for (auto &configCriterionType : configCriterionTypes) {
191 if (configCriterionType.name == criterion.typeName) {
192 criterionType = configCriterionType;
193 break;
194 }
195 }
196 ALOG_ASSERT(not criterionType.name.empty(), "Invalid criterion type for %s",
197 criterion.name.c_str());
198 mPolicyParameterMgr->addCriterion(criterion.name, criterionType.isInclusive,
199 criterionType.valuePairs,
200 criterion.defaultLiteralValue);
201 }
202 };
203
204 loadCriteria(result.parsedConfig->criteria, result.parsedConfig->criterionTypes);
François Gaffiedc7553f2018-11-02 10:39:57 +0100205 return result.nbSkippedElement == 0? NO_ERROR : BAD_VALUE;
206}
207
208DeviceVector Engine::getDevicesForProductStrategy(product_strategy_t ps) const
209{
210 const auto productStrategies = getProductStrategies();
211 if (productStrategies.find(ps) == productStrategies.end()) {
212 ALOGE("%s: Trying to get device on invalid strategy %d", __FUNCTION__, ps);
213 return {};
214 }
Eric Laurentaf377772019-03-29 14:50:21 -0700215 const DeviceVector availableOutputDevices = getApmObserver()->getAvailableOutputDevices();
François Gaffiedc7553f2018-11-02 10:39:57 +0100216 const SwAudioOutputCollection &outputs = getApmObserver()->getOutputs();
jiabin12dc6b02019-10-01 09:38:30 -0700217 DeviceTypeSet availableOutputDevicesTypes = availableOutputDevices.types();
François Gaffiedc7553f2018-11-02 10:39:57 +0100218
219 /** This is the only case handled programmatically because the PFW is unable to know the
220 * activity of streams.
221 *
222 * -While media is playing on a remote device, use the the sonification behavior.
223 * Note that we test this usecase before testing if media is playing because
224 * the isStreamActive() method only informs about the activity of a stream, not
225 * if it's for local playback. Note also that we use the same delay between both tests
226 *
227 * -When media is not playing anymore, fall back on the sonification behavior
228 */
jiabin12dc6b02019-10-01 09:38:30 -0700229 DeviceTypeSet deviceTypes;
François Gaffiedc7553f2018-11-02 10:39:57 +0100230 if (ps == getProductStrategyForStream(AUDIO_STREAM_NOTIFICATION) &&
231 !is_state_in_call(getPhoneState()) &&
Eric Laurent83d17c22019-04-02 17:10:01 -0700232 !outputs.isActiveRemotely(toVolumeSource(AUDIO_STREAM_MUSIC),
François Gaffie1c878552018-11-22 16:53:21 +0100233 SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY) &&
Eric Laurent83d17c22019-04-02 17:10:01 -0700234 outputs.isActive(toVolumeSource(AUDIO_STREAM_MUSIC),
François Gaffie1c878552018-11-22 16:53:21 +0100235 SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY)) {
François Gaffiedc7553f2018-11-02 10:39:57 +0100236 product_strategy_t strategyForMedia =
237 getProductStrategyForStream(AUDIO_STREAM_MUSIC);
jiabin12dc6b02019-10-01 09:38:30 -0700238 deviceTypes = productStrategies.getDeviceTypesForProductStrategy(strategyForMedia);
François Gaffiedc7553f2018-11-02 10:39:57 +0100239 } else if (ps == getProductStrategyForStream(AUDIO_STREAM_ACCESSIBILITY) &&
Eric Laurent83d17c22019-04-02 17:10:01 -0700240 (outputs.isActive(toVolumeSource(AUDIO_STREAM_RING)) ||
241 outputs.isActive(toVolumeSource(AUDIO_STREAM_ALARM)))) {
François Gaffiedc7553f2018-11-02 10:39:57 +0100242 // do not route accessibility prompts to a digital output currently configured with a
243 // compressed format as they would likely not be mixed and dropped.
244 // Device For Sonification conf file has HDMI, SPDIF and HDMI ARC unreacheable.
245 product_strategy_t strategyNotification = getProductStrategyForStream(AUDIO_STREAM_RING);
jiabin12dc6b02019-10-01 09:38:30 -0700246 deviceTypes = productStrategies.getDeviceTypesForProductStrategy(strategyNotification);
François Gaffiedc7553f2018-11-02 10:39:57 +0100247 } else {
jiabin12dc6b02019-10-01 09:38:30 -0700248 deviceTypes = productStrategies.getDeviceTypesForProductStrategy(ps);
François Gaffiedc7553f2018-11-02 10:39:57 +0100249 }
jiabin12dc6b02019-10-01 09:38:30 -0700250 if (deviceTypes.empty() ||
251 Intersection(deviceTypes, availableOutputDevicesTypes).empty()) {
François Gaffiec60c3692019-08-09 15:41:24 +0200252 auto defaultDevice = getApmObserver()->getDefaultOutputDevice();
253 ALOG_ASSERT(defaultDevice != nullptr, "no valid default device defined");
254 return DeviceVector(defaultDevice);
François Gaffiedc7553f2018-11-02 10:39:57 +0100255 }
jiabin12dc6b02019-10-01 09:38:30 -0700256 if (/*device_distinguishes_on_address(*deviceTypes.begin())*/ isSingleDeviceType(
257 deviceTypes, AUDIO_DEVICE_OUT_BUS)) {
François Gaffiedc7553f2018-11-02 10:39:57 +0100258 // We do expect only one device for these types of devices
259 // Criterion device address garantee this one is available
260 // If this criterion is not wished, need to ensure this device is available
261 const String8 address(productStrategies.getDeviceAddressForProductStrategy(ps).c_str());
jiabin12dc6b02019-10-01 09:38:30 -0700262 ALOGV("%s:device %s %s %d",
263 __FUNCTION__, dumpDeviceTypes(deviceTypes).c_str(), address.c_str(), ps);
264 auto busDevice = availableOutputDevices.getDevice(
265 *deviceTypes.begin(), address, AUDIO_FORMAT_DEFAULT);
François Gaffiec60c3692019-08-09 15:41:24 +0200266 if (busDevice == nullptr) {
jiabin12dc6b02019-10-01 09:38:30 -0700267 ALOGE("%s:unavailable device %s %s, fallback on default", __func__,
268 dumpDeviceTypes(deviceTypes).c_str(), address.c_str());
François Gaffiec60c3692019-08-09 15:41:24 +0200269 auto defaultDevice = getApmObserver()->getDefaultOutputDevice();
270 ALOG_ASSERT(defaultDevice != nullptr, "Default Output Device NOT available");
271 return DeviceVector(defaultDevice);
272 }
jiabin12dc6b02019-10-01 09:38:30 -0700273 return DeviceVector(busDevice);
François Gaffiedc7553f2018-11-02 10:39:57 +0100274 }
jiabin12dc6b02019-10-01 09:38:30 -0700275 ALOGV("%s:device %s %d", __FUNCTION__, dumpDeviceTypes(deviceTypes).c_str(), ps);
276 return availableOutputDevices.getDevicesFromTypes(deviceTypes);
François Gaffiedc7553f2018-11-02 10:39:57 +0100277}
278
279DeviceVector Engine::getOutputDevicesForAttributes(const audio_attributes_t &attributes,
280 const sp<DeviceDescriptor> &preferredDevice,
281 bool fromCache) const
282{
283 // First check for explict routing device
284 if (preferredDevice != nullptr) {
285 ALOGV("%s explicit Routing on device %s", __func__, preferredDevice->toString().c_str());
286 return DeviceVector(preferredDevice);
287 }
François Gaffiec005e562018-11-06 15:04:49 +0100288 product_strategy_t strategy = getProductStrategyForAttributes(attributes);
Eric Laurentaf377772019-03-29 14:50:21 -0700289 const DeviceVector availableOutputDevices = getApmObserver()->getAvailableOutputDevices();
François Gaffiec005e562018-11-06 15:04:49 +0100290 const SwAudioOutputCollection &outputs = getApmObserver()->getOutputs();
François Gaffiedc7553f2018-11-02 10:39:57 +0100291 //
François Gaffiec005e562018-11-06 15:04:49 +0100292 // @TODO: what is the priority of explicit routing? Shall it be considered first as it used to
293 // be by APM?
François Gaffiedc7553f2018-11-02 10:39:57 +0100294 //
François Gaffiec005e562018-11-06 15:04:49 +0100295 // Honor explicit routing requests only if all active clients have a preferred route in which
296 // case the last active client route is used
297 sp<DeviceDescriptor> device = findPreferredDevice(outputs, strategy, availableOutputDevices);
298 if (device != nullptr) {
299 return DeviceVector(device);
300 }
Francois Gaffie6f52ff92020-08-25 08:53:53 +0200301 return fromCache? getCachedDevices(strategy) : getDevicesForProductStrategy(strategy);
302}
François Gaffiec005e562018-11-06 15:04:49 +0100303
Francois Gaffie6f52ff92020-08-25 08:53:53 +0200304DeviceVector Engine::getCachedDevices(product_strategy_t ps) const
305{
306 return mDevicesForStrategies.find(ps) != mDevicesForStrategies.end() ?
307 mDevicesForStrategies.at(ps) : DeviceVector{};
François Gaffiedc7553f2018-11-02 10:39:57 +0100308}
309
310DeviceVector Engine::getOutputDevicesForStream(audio_stream_type_t stream, bool fromCache) const
311{
312 auto attributes = EngineBase::getAttributesForStreamType(stream);
313 return getOutputDevicesForAttributes(attributes, nullptr, fromCache);
314}
315
316sp<DeviceDescriptor> Engine::getInputDeviceForAttributes(const audio_attributes_t &attr,
yuanjiahsu0735bf32021-03-18 08:12:54 +0800317 uid_t uid,
Jan Sebechlebsky1a80c062022-08-09 15:21:18 +0200318 audio_session_t session,
Mikhail Naganovbfac5832019-03-05 16:55:28 -0800319 sp<AudioPolicyMix> *mix) const
François Gaffiedc7553f2018-11-02 10:39:57 +0100320{
François Gaffiec005e562018-11-06 15:04:49 +0100321 const auto &policyMixes = getApmObserver()->getAudioPolicyMixCollection();
Eric Laurentaf377772019-03-29 14:50:21 -0700322 const auto availableInputDevices = getApmObserver()->getAvailableInputDevices();
François Gaffiec005e562018-11-06 15:04:49 +0100323 const auto &inputs = getApmObserver()->getInputs();
François Gaffiedc7553f2018-11-02 10:39:57 +0100324 std::string address;
325 //
François Gaffiec005e562018-11-06 15:04:49 +0100326 // Explicit Routing ??? what is the priority of explicit routing? Shall it be considered
327 // first as it used to be by APM?
François Gaffiedc7553f2018-11-02 10:39:57 +0100328 //
François Gaffiec005e562018-11-06 15:04:49 +0100329 // Honor explicit routing requests only if all active clients have a preferred route in which
330 // case the last active client route is used
331 sp<DeviceDescriptor> device =
332 findPreferredDevice(inputs, attr.source, availableInputDevices);
333 if (device != nullptr) {
334 return device;
335 }
336
Jan Sebechlebsky28ed9452022-09-15 17:57:42 +0200337 device = policyMixes.getDeviceAndMixForInputSource(attr,
yuanjiahsu0735bf32021-03-18 08:12:54 +0800338 availableInputDevices,
339 uid,
Jan Sebechlebsky1a80c062022-08-09 15:21:18 +0200340 session,
yuanjiahsu0735bf32021-03-18 08:12:54 +0800341 mix);
François Gaffiec005e562018-11-06 15:04:49 +0100342 if (device != nullptr) {
343 return device;
344 }
345
François Gaffiedc7553f2018-11-02 10:39:57 +0100346 audio_devices_t deviceType = getPropertyForKey<audio_devices_t, audio_source_t>(attr.source);
347
348 if (audio_is_remote_submix_device(deviceType)) {
349 address = "0";
350 std::size_t pos;
351 std::string tags { attr.tags };
352 if ((pos = tags.find("addr=")) != std::string::npos) {
353 address = tags.substr(pos + std::strlen("addr="));
354 }
355 }
François Gaffiec005e562018-11-06 15:04:49 +0100356 return availableInputDevices.getDevice(deviceType, String8(address.c_str()), AUDIO_FORMAT_DEFAULT);
François Gaffiedc7553f2018-11-02 10:39:57 +0100357}
358
359void Engine::updateDeviceSelectionCache()
360{
361 for (const auto &iter : getProductStrategies()) {
362 const auto &strategy = iter.second;
363 mDevicesForStrategies[strategy->getId()] = getDevicesForProductStrategy(strategy->getId());
364 }
365}
366
François Gaffief1e95082018-11-02 13:53:31 +0100367void Engine::setDeviceAddressForProductStrategy(product_strategy_t strategy,
368 const std::string &address)
369{
370 if (getProductStrategies().find(strategy) == getProductStrategies().end()) {
371 ALOGE("%s: Trying to set address %s on invalid strategy %d", __FUNCTION__, address.c_str(),
372 strategy);
373 return;
374 }
375 getProductStrategies().at(strategy)->setDeviceAddress(address);
376}
377
Francois Gaffiea12de212021-10-22 10:54:33 +0200378bool Engine::setDeviceTypesForProductStrategy(product_strategy_t strategy, uint64_t devices)
François Gaffief1e95082018-11-02 13:53:31 +0100379{
380 if (getProductStrategies().find(strategy) == getProductStrategies().end()) {
Francois Gaffiea12de212021-10-22 10:54:33 +0200381 ALOGE("%s: set device %" PRId64 " on invalid strategy %d", __FUNCTION__, devices, strategy);
François Gaffief1e95082018-11-02 13:53:31 +0100382 return false;
383 }
Francois Gaffiea12de212021-10-22 10:54:33 +0200384 // Here device matches the criterion value, need to rebuitd android device types;
385 DeviceTypeSet types =
386 mPolicyParameterMgr->convertDeviceCriterionValueToDeviceTypes(devices, true /*isOut*/);
387 getProductStrategies().at(strategy)->setDeviceTypes(types);
François Gaffief1e95082018-11-02 13:53:31 +0100388 return true;
389}
390
Francois Gaffiea12de212021-10-22 10:54:33 +0200391bool Engine::setDeviceForInputSource(const audio_source_t &inputSource, uint64_t device)
392{
393 DeviceTypeSet types = mPolicyParameterMgr->convertDeviceCriterionValueToDeviceTypes(
394 device, false /*isOut*/);
395 ALOG_ASSERT(types.size() <= 1, "one input device expected at most");
396 audio_devices_t deviceType = types.empty() ? AUDIO_DEVICE_IN_DEFAULT : *types.begin();
397 return setPropertyForKey<audio_devices_t, audio_source_t>(deviceType, inputSource);
398}
399
François Gaffie20f06f92015-03-24 09:01:14 +0100400template <>
Mikhail Naganove13c6792019-05-14 10:32:51 -0700401EngineInterface *Engine::queryInterface()
François Gaffie20f06f92015-03-24 09:01:14 +0100402{
François Gaffiedc7553f2018-11-02 10:39:57 +0100403 return this;
François Gaffie20f06f92015-03-24 09:01:14 +0100404}
405
406template <>
407AudioPolicyPluginInterface *Engine::queryInterface()
408{
François Gaffiedc7553f2018-11-02 10:39:57 +0100409 return this;
François Gaffie20f06f92015-03-24 09:01:14 +0100410}
411
412} // namespace audio_policy
413} // namespace android
414
415