1 /*
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_AudioPolicyMix"
18 //#define LOG_NDEBUG 0
19
20 #include <algorithm>
21 #include <iterator>
22 #include <optional>
23 #include <regex>
24 #include <vector>
25 #include "AudioPolicyMix.h"
26 #include "TypeConverter.h"
27 #include "HwModule.h"
28 #include "PolicyAudioPort.h"
29 #include "IOProfile.h"
30 #include <AudioOutputDescriptor.h>
31 #include <android_media_audiopolicy.h>
32
33 namespace audiopolicy_flags = android::media::audiopolicy;
34
35 namespace android {
36 namespace {
37
matchAddressToTags(const audio_attributes_t & attr,const String8 & addr)38 bool matchAddressToTags(const audio_attributes_t& attr, const String8& addr) {
39 std::optional<std::string> tagAddress = extractAddressFromAudioAttributes(attr);
40 return tagAddress.has_value() && tagAddress->compare(addr.c_str()) == 0;
41 }
42
43 // Returns true if the criterion matches.
44 // The exclude criteria are handled in the same way as positive
45 // ones - only condition is matched (the function will return
46 // same result both for RULE_MATCH_X and RULE_EXCLUDE_X).
isCriterionMatched(const AudioMixMatchCriterion & criterion,const audio_attributes_t & attr,const uid_t uid,const audio_session_t session)47 bool isCriterionMatched(const AudioMixMatchCriterion& criterion,
48 const audio_attributes_t& attr,
49 const uid_t uid,
50 const audio_session_t session) {
51 uint32_t ruleWithoutExclusion = criterion.mRule & ~RULE_EXCLUSION_MASK;
52 switch(ruleWithoutExclusion) {
53 case RULE_MATCH_ATTRIBUTE_USAGE:
54 return criterion.mValue.mUsage == attr.usage;
55 case RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET:
56 return criterion.mValue.mSource == attr.source;
57 case RULE_MATCH_UID:
58 return criterion.mValue.mUid == uid;
59 case RULE_MATCH_USERID:
60 {
61 userid_t userId = multiuser_get_user_id(uid);
62 return criterion.mValue.mUserId == userId;
63 }
64 case RULE_MATCH_AUDIO_SESSION_ID:
65 return criterion.mValue.mAudioSessionId == session;
66 }
67 ALOGE("Encountered invalid mix rule 0x%x", criterion.mRule);
68 return false;
69 }
70
71 // Returns true if vector of criteria is matched:
72 // - If any of the exclude criteria is matched the criteria doesn't match.
73 // - Otherwise, for each 'dimension' of positive rule present
74 // (usage, capture preset, uid, userid...) at least one rule must match
75 // for the criteria to match.
areMixCriteriaMatched(const std::vector<AudioMixMatchCriterion> & criteria,const audio_attributes_t & attr,const uid_t uid,const audio_session_t session)76 bool areMixCriteriaMatched(const std::vector<AudioMixMatchCriterion>& criteria,
77 const audio_attributes_t& attr,
78 const uid_t uid,
79 const audio_session_t session) {
80 // If any of the exclusion criteria are matched the mix doesn't match.
81 auto isMatchingExcludeCriterion = [&](const AudioMixMatchCriterion& c) {
82 return c.isExcludeCriterion() && isCriterionMatched(c, attr, uid, session);
83 };
84 if (std::any_of(criteria.begin(), criteria.end(), isMatchingExcludeCriterion)) {
85 return false;
86 }
87
88 uint32_t presentPositiveRules = 0; // Bitmask of all present positive criteria.
89 uint32_t matchedPositiveRules = 0; // Bitmask of all matched positive criteria.
90 for (const auto& criterion : criteria) {
91 if (criterion.isExcludeCriterion()) {
92 continue;
93 }
94 presentPositiveRules |= criterion.mRule;
95 if (isCriterionMatched(criterion, attr, uid, session)) {
96 matchedPositiveRules |= criterion.mRule;
97 }
98 }
99 return presentPositiveRules == matchedPositiveRules;
100 }
101
102 // Consistency checks: for each "dimension" of rules (usage, uid...), we can
103 // only have MATCH rules, or EXCLUDE rules in each dimension, not a combination.
areMixCriteriaConsistent(const std::vector<AudioMixMatchCriterion> & criteria)104 bool areMixCriteriaConsistent(const std::vector<AudioMixMatchCriterion>& criteria) {
105 std::set<uint32_t> positiveCriteria;
106 for (const AudioMixMatchCriterion& c : criteria) {
107 if (c.isExcludeCriterion()) {
108 continue;
109 }
110 positiveCriteria.insert(c.mRule);
111 }
112
113 auto isConflictingCriterion = [&positiveCriteria](const AudioMixMatchCriterion& c) {
114 uint32_t ruleWithoutExclusion = c.mRule & ~RULE_EXCLUSION_MASK;
115 return c.isExcludeCriterion() &&
116 (positiveCriteria.find(ruleWithoutExclusion) != positiveCriteria.end());
117 };
118 return std::none_of(criteria.begin(), criteria.end(), isConflictingCriterion);
119 }
120
121 template <typename Predicate>
EraseCriteriaIf(std::vector<AudioMixMatchCriterion> & v,const Predicate & predicate)122 void EraseCriteriaIf(std::vector<AudioMixMatchCriterion>& v,
123 const Predicate& predicate) {
124 v.erase(std::remove_if(v.begin(), v.end(), predicate), v.end());
125 }
126
127 } // namespace
128
dump(String8 * dst,int spaces,int index) const129 void AudioPolicyMix::dump(String8 *dst, int spaces, int index) const
130 {
131 dst->appendFormat("%*sAudio Policy Mix %d (%p):\n", spaces, "", index + 1, this);
132 std::string mixTypeLiteral;
133 if (!MixTypeConverter::toString(mMixType, mixTypeLiteral)) {
134 ALOGE("%s: failed to convert mix type %d", __FUNCTION__, mMixType);
135 return;
136 }
137 dst->appendFormat("%*s- mix type: %s\n", spaces, "", mixTypeLiteral.c_str());
138
139 std::string routeFlagLiteral;
140 RouteFlagTypeConverter::maskToString(mRouteFlags, routeFlagLiteral);
141 dst->appendFormat("%*s- Route Flags: %s\n", spaces, "", routeFlagLiteral.c_str());
142
143 dst->appendFormat("%*s- device type: %s\n", spaces, "", toString(mDeviceType).c_str());
144
145 dst->appendFormat("%*s- device address: %s\n", spaces, "", mDeviceAddress.c_str());
146
147 dst->appendFormat("%*s- output: %d\n", spaces, "",
148 mOutput == nullptr ? 0 : mOutput->mIoHandle);
149
150 int indexCriterion = 0;
151 for (const auto &criterion : mCriteria) {
152 dst->appendFormat("%*s- Criterion %d: ", spaces + 2, "", indexCriterion++);
153
154 std::string ruleType, ruleValue;
155 bool unknownRule = !RuleTypeConverter::toString(criterion.mRule, ruleType);
156 switch (criterion.mRule & ~RULE_EXCLUSION_MASK) { // no need to match RULE_EXCLUDE_...
157 case RULE_MATCH_ATTRIBUTE_USAGE:
158 UsageTypeConverter::toString(criterion.mValue.mUsage, ruleValue);
159 break;
160 case RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET:
161 SourceTypeConverter::toString(criterion.mValue.mSource, ruleValue);
162 break;
163 case RULE_MATCH_UID:
164 ruleValue = std::to_string(criterion.mValue.mUid);
165 break;
166 case RULE_MATCH_USERID:
167 ruleValue = std::to_string(criterion.mValue.mUserId);
168 break;
169 case RULE_MATCH_AUDIO_SESSION_ID:
170 ruleValue = std::to_string(criterion.mValue.mAudioSessionId);
171 break;
172 default:
173 unknownRule = true;
174 }
175
176 if (!unknownRule) {
177 dst->appendFormat("%s %s\n", ruleType.c_str(), ruleValue.c_str());
178 } else {
179 dst->appendFormat("Unknown rule type value 0x%x\n", criterion.mRule);
180 }
181 }
182 }
183
registerMix(const AudioMix & mix,const sp<SwAudioOutputDescriptor> & desc)184 status_t AudioPolicyMixCollection::registerMix(const AudioMix& mix,
185 const sp<SwAudioOutputDescriptor>& desc)
186 {
187 for (size_t i = 0; i < size(); i++) {
188 const sp<AudioPolicyMix>& registeredMix = itemAt(i);
189 if (mix.mDeviceType == registeredMix->mDeviceType
190 && mix.mDeviceAddress.compare(registeredMix->mDeviceAddress) == 0
191 && is_mix_loopback(mix.mRouteFlags)) {
192 ALOGE("registerMix(): mix already registered for dev=0x%x addr=%s",
193 mix.mDeviceType, mix.mDeviceAddress.c_str());
194 return BAD_VALUE;
195 }
196 if (audiopolicy_flags::audio_mix_ownership()) {
197 if (mix.mToken == registeredMix->mToken) {
198 ALOGE("registerMix(): same mix already registered - skipping");
199 return BAD_VALUE;
200 }
201 }
202 }
203 if (!areMixCriteriaConsistent(mix.mCriteria)) {
204 ALOGE("registerMix(): Mix contains inconsistent criteria "
205 "(MATCH & EXCLUDE criteria of the same type)");
206 return BAD_VALUE;
207 }
208 sp<AudioPolicyMix> policyMix = sp<AudioPolicyMix>::make(mix);
209 add(policyMix);
210 ALOGD("registerMix(): adding mix for dev=0x%x addr=%s",
211 policyMix->mDeviceType, policyMix->mDeviceAddress.c_str());
212
213 if (desc != nullptr) {
214 desc->mPolicyMix = policyMix;
215 policyMix->setOutput(desc);
216 }
217 return NO_ERROR;
218 }
219
unregisterMix(const AudioMix & mix)220 status_t AudioPolicyMixCollection::unregisterMix(const AudioMix& mix)
221 {
222 for (size_t i = 0; i < size(); i++) {
223 const sp<AudioPolicyMix>& registeredMix = itemAt(i);
224 if (audiopolicy_flags::audio_mix_ownership()) {
225 if (mix.mToken == registeredMix->mToken) {
226 ALOGD("unregisterMix(): removing mix for dev=0x%x addr=%s",
227 mix.mDeviceType, mix.mDeviceAddress.c_str());
228 removeAt(i);
229 return NO_ERROR;
230 }
231 } else {
232 if (mix.mDeviceType == registeredMix->mDeviceType
233 && mix.mDeviceAddress.compare(registeredMix->mDeviceAddress) == 0) {
234 ALOGD("unregisterMix(): removing mix for dev=0x%x addr=%s",
235 mix.mDeviceType, mix.mDeviceAddress.c_str());
236 removeAt(i);
237 return NO_ERROR;
238 }
239 }
240 }
241
242 ALOGE("unregisterMix(): mix not registered for dev=0x%x addr=%s",
243 mix.mDeviceType, mix.mDeviceAddress.c_str());
244 return BAD_VALUE;
245 }
246
updateMix(const AudioMix & mix,const std::vector<AudioMixMatchCriterion> & updatedCriteria)247 status_t AudioPolicyMixCollection::updateMix(
248 const AudioMix& mix, const std::vector<AudioMixMatchCriterion>& updatedCriteria) {
249 if (!areMixCriteriaConsistent(mix.mCriteria)) {
250 ALOGE("updateMix(): updated criteria are not consistent "
251 "(MATCH & EXCLUDE criteria of the same type)");
252 return BAD_VALUE;
253 }
254
255 for (size_t i = 0; i < size(); i++) {
256 const sp<AudioPolicyMix>& registeredMix = itemAt(i);
257 if (mix.mDeviceType == registeredMix->mDeviceType &&
258 mix.mDeviceAddress.compare(registeredMix->mDeviceAddress) == 0 &&
259 mix.mRouteFlags == registeredMix->mRouteFlags) {
260 registeredMix->mCriteria = updatedCriteria;
261 ALOGV("updateMix(): updated mix for dev=0x%x addr=%s", mix.mDeviceType,
262 mix.mDeviceAddress.c_str());
263 return NO_ERROR;
264 }
265 }
266
267 ALOGE("updateMix(): mix not registered for dev=0x%x addr=%s", mix.mDeviceType,
268 mix.mDeviceAddress.c_str());
269 return BAD_VALUE;
270 }
271
getAudioPolicyMix(audio_devices_t deviceType,const String8 & address,sp<AudioPolicyMix> & policyMix) const272 status_t AudioPolicyMixCollection::getAudioPolicyMix(audio_devices_t deviceType,
273 const String8& address, sp<AudioPolicyMix> &policyMix) const
274 {
275
276 ALOGV("getAudioPolicyMix() for dev=0x%x addr=%s", deviceType, address.c_str());
277 for (ssize_t i = 0; i < size(); i++) {
278 // Workaround: when an in audio policy is registered, it opens an output
279 // that tries to find the audio policy, thus the device must be ignored.
280 if (itemAt(i)->mDeviceAddress.compare(address) == 0) {
281 policyMix = itemAt(i);
282 ALOGV("getAudioPolicyMix: found mix %zu match (devType=0x%x addr=%s)",
283 i, deviceType, address.c_str());
284 return NO_ERROR;
285 }
286 }
287
288 ALOGE("getAudioPolicyMix(): mix not registered for dev=0x%x addr=%s",
289 deviceType, address.c_str());
290 return BAD_VALUE;
291 }
292
closeOutput(sp<SwAudioOutputDescriptor> & desc,const SwAudioOutputCollection & allOutputs)293 void AudioPolicyMixCollection::closeOutput(sp<SwAudioOutputDescriptor> &desc,
294 const SwAudioOutputCollection& allOutputs)
295 {
296 for (size_t i = 0; i < size(); i++) {
297 sp<AudioPolicyMix> policyMix = itemAt(i);
298 if (policyMix->getOutput() != desc) {
299 continue;
300 }
301 policyMix->clearOutput();
302 if (policyMix->mRouteFlags != MIX_ROUTE_FLAG_RENDER) {
303 continue;
304 }
305 auto device = desc->supportedDevices().getDevice(
306 policyMix->mDeviceType, policyMix->mDeviceAddress, AUDIO_FORMAT_DEFAULT);
307 if (device == nullptr) {
308 // This must not happen
309 ALOGE("%s, the rerouted device is not found", __func__);
310 continue;
311 }
312 // Restore the policy mix mix output to the first opened output supporting a route to
313 // the mix device. This is because the current mix output can be changed to a direct output.
314 for (size_t j = 0; j < allOutputs.size(); ++j) {
315 if (allOutputs[i] != desc && !allOutputs[i]->isDuplicated() &&
316 allOutputs[i]->supportedDevices().contains(device)) {
317 policyMix->setOutput(allOutputs[i]);
318 break;
319 }
320 }
321 }
322 }
323
getOutputForAttr(const audio_attributes_t & attributes,const audio_config_base_t & config,const uid_t uid,const audio_session_t session,audio_output_flags_t flags,const DeviceVector & availableOutputDevices,const sp<DeviceDescriptor> & requestedDevice,sp<AudioPolicyMix> & primaryMix,std::vector<sp<AudioPolicyMix>> * secondaryMixes,bool & usePrimaryOutputFromPolicyMixes)324 status_t AudioPolicyMixCollection::getOutputForAttr(
325 const audio_attributes_t& attributes, const audio_config_base_t& config, const uid_t uid,
326 const audio_session_t session,
327 audio_output_flags_t flags,
328 const DeviceVector &availableOutputDevices,
329 const sp<DeviceDescriptor>& requestedDevice,
330 sp<AudioPolicyMix> &primaryMix,
331 std::vector<sp<AudioPolicyMix>> *secondaryMixes,
332 bool& usePrimaryOutputFromPolicyMixes)
333 {
334 ALOGV("getOutputForAttr() querying %zu mixes:", size());
335 primaryMix.clear();
336 bool mixesDisallowsRequestedDevice = false;
337 const bool isMmapRequested = (flags & AUDIO_OUTPUT_FLAG_MMAP_NOIRQ);
338 for (size_t i = 0; i < size(); i++) {
339 sp<AudioPolicyMix> policyMix = itemAt(i);
340 const bool primaryOutputMix = !is_mix_loopback_render(policyMix->mRouteFlags);
341 sp<DeviceDescriptor> mixDevice = getOutputDeviceForMix(policyMix.get(),
342 availableOutputDevices);
343 if (mixDisallowsRequestedDevice(policyMix.get(), requestedDevice, mixDevice, uid)) {
344 ALOGV("%s: Mix %zu: does not allows device", __func__, i);
345 mixesDisallowsRequestedDevice = true;
346 }
347
348 if (!primaryOutputMix && isMmapRequested) {
349 // AAudio does not support MMAP_NO_IRQ loopback render, and there is no way with
350 // the current MmapStreamInterface::start to reject a specific client added to a shared
351 // mmap stream.
352 // As a result all MMAP_NOIRQ requests have to be rejected when an loopback render
353 // policy is present. That ensures no shared mmap stream is used when an loopback
354 // render policy is registered.
355 ALOGD("%s: Rejecting MMAP_NOIRQ request due to LOOPBACK|RENDER mix present.", __func__);
356 return INVALID_OPERATION;
357 }
358
359 if (primaryOutputMix && primaryMix != nullptr) {
360 ALOGV("%s: Skiping %zu: Primary output already found", __func__, i);
361 continue; // Primary output already found
362 }
363
364 if(!mixMatch(policyMix.get(), i, attributes, config, uid, session)) {
365 ALOGV("%s: Mix %zu: does not match", __func__, i);
366 continue; // skip the mix
367 }
368
369 if (isMmapRequested) {
370 if (is_mix_loopback(policyMix->mRouteFlags)) {
371 // AAudio MMAP_NOIRQ streams cannot be routed to loopback/loopback+render
372 // using dynamic audio policy.
373 ALOGD("%s: Rejecting MMAP_NOIRQ request matched to loopback dynamic "
374 "audio policy mix.", __func__);
375 return INVALID_OPERATION;
376 }
377 }
378
379 if (mixDevice != nullptr && mixDevice->equals(requestedDevice)) {
380 ALOGV("%s: Mix %zu: requested device mathches", __func__, i);
381 mixesDisallowsRequestedDevice = false;
382 }
383
384 if (primaryOutputMix) {
385 primaryMix = policyMix;
386 ALOGV("%s: Mix %zu: set primary desc", __func__, i);
387 } else {
388 ALOGV("%s: Add a secondary desc %zu", __func__, i);
389 if (secondaryMixes != nullptr) {
390 secondaryMixes->push_back(policyMix);
391 }
392 }
393 }
394
395 // Explicit routing is higher priority than dynamic policy primary output, but policy may
396 // explicitly deny it
397 usePrimaryOutputFromPolicyMixes =
398 (mixesDisallowsRequestedDevice || requestedDevice == nullptr) && primaryMix != nullptr;
399
400 return NO_ERROR;
401 }
402
getOutputDeviceForMix(const AudioMix * mix,const DeviceVector & availableOutputDevices)403 sp<DeviceDescriptor> AudioPolicyMixCollection::getOutputDeviceForMix(const AudioMix* mix,
404 const DeviceVector& availableOutputDevices) {
405 ALOGV("%s: device (0x%x, addr=%s) forced by mix", __func__, mix->mDeviceType,
406 mix->mDeviceAddress.c_str());
407 return availableOutputDevices.getDevice(mix->mDeviceType, mix->mDeviceAddress,
408 AUDIO_FORMAT_DEFAULT);
409 }
410
mixDisallowsRequestedDevice(const AudioMix * mix,const sp<DeviceDescriptor> & requestedDevice,const sp<DeviceDescriptor> & mixDevice,const uid_t uid)411 bool AudioPolicyMixCollection::mixDisallowsRequestedDevice(const AudioMix* mix,
412 const sp<DeviceDescriptor>& requestedDevice,
413 const sp<DeviceDescriptor>& mixDevice,
414 const uid_t uid) {
415 if (requestedDevice == nullptr || mixDevice == nullptr) {
416 return false;
417 }
418
419 return is_mix_disallows_preferred_device(mix->mRouteFlags)
420 && requestedDevice->equals(mixDevice)
421 && mix->hasUserIdRule(false /* match */, multiuser_get_user_id(uid));
422 }
423
mixMatch(const AudioMix * mix,size_t mixIndex,const audio_attributes_t & attributes,const audio_config_base_t & config,uid_t uid,audio_session_t session)424 bool AudioPolicyMixCollection::mixMatch(const AudioMix* mix, size_t mixIndex,
425 const audio_attributes_t& attributes, const audio_config_base_t& config,
426 uid_t uid, audio_session_t session) {
427
428 if (mix->mMixType == MIX_TYPE_PLAYERS) {
429 // Loopback render mixes are created from a public API and thus restricted
430 // to non sensible audio that have not opted out.
431 if (is_mix_loopback_render(mix->mRouteFlags)) {
432 if (!(attributes.usage == AUDIO_USAGE_UNKNOWN ||
433 attributes.usage == AUDIO_USAGE_MEDIA ||
434 attributes.usage == AUDIO_USAGE_GAME ||
435 attributes.usage == AUDIO_USAGE_VOICE_COMMUNICATION)) {
436 return false;
437 }
438 auto hasFlag = [](auto flags, auto flag) { return (flags & flag) == flag; };
439 if (hasFlag(attributes.flags, AUDIO_FLAG_NO_SYSTEM_CAPTURE)) {
440 return false;
441 }
442
443 if (attributes.usage == AUDIO_USAGE_VOICE_COMMUNICATION) {
444 if (!mix->mVoiceCommunicationCaptureAllowed) {
445 return false;
446 }
447 } else if (!mix->mAllowPrivilegedMediaPlaybackCapture &&
448 hasFlag(attributes.flags, AUDIO_FLAG_NO_MEDIA_PROJECTION)) {
449 return false;
450 }
451 }
452
453 // Permit match only if requested format and mix format are PCM and can be format
454 // adapted by the mixer, or are the same (compressed) format.
455 if (!is_mix_loopback(mix->mRouteFlags) &&
456 !((audio_is_linear_pcm(config.format) && audio_is_linear_pcm(mix->mFormat.format)) ||
457 (config.format == mix->mFormat.format)) &&
458 config.format != AUDIO_CONFIG_BASE_INITIALIZER.format) {
459 return false;
460 }
461
462 // if there is an address match, prioritize that match
463 if (matchAddressToTags(attributes, mix->mDeviceAddress)
464 || areMixCriteriaMatched(mix->mCriteria, attributes, uid, session)) {
465 ALOGV("\tgetOutputForAttr will use mix %zu", mixIndex);
466 return true;
467 }
468 } else if (mix->mMixType == MIX_TYPE_RECORDERS) {
469 if (attributes.usage == AUDIO_USAGE_VIRTUAL_SOURCE &&
470 matchAddressToTags(attributes, mix->mDeviceAddress)) {
471 return true;
472 }
473 }
474 return false;
475 }
476
getDeviceAndMixForOutput(const sp<SwAudioOutputDescriptor> & output,const DeviceVector & availableOutputDevices)477 sp<DeviceDescriptor> AudioPolicyMixCollection::getDeviceAndMixForOutput(
478 const sp<SwAudioOutputDescriptor> &output,
479 const DeviceVector &availableOutputDevices)
480 {
481 for (size_t i = 0; i < size(); i++) {
482 if (itemAt(i)->getOutput() == output) {
483 // This Desc is involved in a Mix, which has the highest prio
484 return getOutputDeviceForMix(itemAt(i).get(), availableOutputDevices);
485 }
486 }
487 return nullptr;
488 }
489
getDeviceAndMixForInputSource(const audio_attributes_t & attributes,const DeviceVector & availDevices,uid_t uid,audio_session_t session,sp<AudioPolicyMix> * policyMix) const490 sp<DeviceDescriptor> AudioPolicyMixCollection::getDeviceAndMixForInputSource(
491 const audio_attributes_t& attributes,
492 const DeviceVector &availDevices,
493 uid_t uid,
494 audio_session_t session,
495 sp<AudioPolicyMix> *policyMix) const
496 {
497 for (size_t i = 0; i < size(); i++) {
498 AudioPolicyMix *mix = itemAt(i).get();
499 if (mix->mMixType != MIX_TYPE_RECORDERS) {
500 continue;
501 }
502 if (areMixCriteriaMatched(mix->mCriteria, attributes, uid, session)) {
503 // Assuming PolicyMix only for remote submix for input
504 // so mix->mDeviceType can only be AUDIO_DEVICE_OUT_REMOTE_SUBMIX.
505 auto mixDevice = availDevices.getDevice(AUDIO_DEVICE_IN_REMOTE_SUBMIX,
506 mix->mDeviceAddress, AUDIO_FORMAT_DEFAULT);
507 if (mixDevice != nullptr) {
508 if (policyMix != nullptr) {
509 *policyMix = mix;
510 }
511 return mixDevice;
512 }
513 }
514 }
515 return nullptr;
516 }
517
getInputMixForAttr(audio_attributes_t attr,sp<AudioPolicyMix> * policyMix)518 status_t AudioPolicyMixCollection::getInputMixForAttr(
519 audio_attributes_t attr, sp<AudioPolicyMix> *policyMix)
520 {
521 std::optional<std::string> address = extractAddressFromAudioAttributes(attr);
522 if (!address.has_value()) {
523 return BAD_VALUE;
524 }
525
526 #ifdef LOG_NDEBUG
527 ALOGV("getInputMixForAttr looking for address %s for source %d\n mixes available:",
528 address->c_str(), attr.source);
529 for (size_t i = 0; i < size(); i++) {
530 const sp<AudioPolicyMix> audioPolicyMix = itemAt(i);
531 ALOGV("\tmix %zu address=%s", i, audioPolicyMix->mDeviceAddress.c_str());
532 }
533 #endif
534
535 size_t index;
536 for (index = 0; index < size(); index++) {
537 const sp<AudioPolicyMix>& registeredMix = itemAt(index);
538 if (address->compare(registeredMix->mDeviceAddress.c_str()) == 0) {
539 ALOGD("getInputMixForAttr found addr=%s dev=0x%x",
540 registeredMix->mDeviceAddress.c_str(), registeredMix->mDeviceType);
541 break;
542 }
543 }
544 if (index == size()) {
545 ALOGW("getInputMixForAttr() no policy for address %s", address->c_str());
546 return BAD_VALUE;
547 }
548 const sp<AudioPolicyMix> audioPolicyMix = itemAt(index);
549
550 if (audioPolicyMix->mMixType != MIX_TYPE_PLAYERS) {
551 ALOGW("getInputMixForAttr() bad policy mix type for address %s", address->c_str());
552 return BAD_VALUE;
553 }
554 if (policyMix != nullptr) {
555 *policyMix = audioPolicyMix;
556 }
557 return NO_ERROR;
558 }
559
setUidDeviceAffinities(uid_t uid,const AudioDeviceTypeAddrVector & devices)560 status_t AudioPolicyMixCollection::setUidDeviceAffinities(uid_t uid,
561 const AudioDeviceTypeAddrVector& devices) {
562 // verify feasibility: for each player mix: if it already contains a
563 // "match uid" rule for this uid, return an error
564 // (adding a uid-device affinity would result in contradictory rules)
565 for (size_t i = 0; i < size(); i++) {
566 const AudioPolicyMix* mix = itemAt(i).get();
567 if (!mix->isDeviceAffinityCompatible()) {
568 continue;
569 }
570 if (mix->hasUidRule(true /*match*/, uid)) {
571 return INVALID_OPERATION;
572 }
573 }
574
575 // remove existing rules for this uid
576 removeUidDeviceAffinities(uid);
577
578 // for each player mix:
579 // IF device is not a target for the mix,
580 // AND it doesn't have a "match uid" rule
581 // THEN add a rule to exclude the uid
582 for (size_t i = 0; i < size(); i++) {
583 AudioPolicyMix *mix = itemAt(i).get();
584 if (!mix->isDeviceAffinityCompatible()) {
585 continue;
586 }
587 // check if this mix goes to a device in the list of devices
588 bool deviceMatch = false;
589 const AudioDeviceTypeAddr mixDevice(mix->mDeviceType, mix->mDeviceAddress.c_str());
590 for (size_t j = 0; j < devices.size(); j++) {
591 if (mixDevice.equals(devices[j])) {
592 deviceMatch = true;
593 break;
594 }
595 }
596 if (!deviceMatch && !mix->hasMatchUidRule()) {
597 // this mix doesn't go to one of the listed devices for the given uid,
598 // and it's not already restricting the mix on a uid,
599 // modify its rules to exclude the uid
600 if (!mix->hasUidRule(false /*match*/, uid)) {
601 // no need to do it again if uid is already excluded
602 mix->setExcludeUid(uid);
603 }
604 }
605 }
606
607 return NO_ERROR;
608 }
609
removeUidDeviceAffinities(uid_t uid)610 status_t AudioPolicyMixCollection::removeUidDeviceAffinities(uid_t uid) {
611 // for each player mix: remove existing rules that match or exclude this uid
612 for (size_t i = 0; i < size(); i++) {
613 AudioPolicyMix *mix = itemAt(i).get();
614 if (!mix->isDeviceAffinityCompatible()) {
615 continue;
616 }
617
618 // is this rule excluding the uid? (not considering uid match rules
619 // as those are not used for uid-device affinity)
620 EraseCriteriaIf(mix->mCriteria, [uid](const AudioMixMatchCriterion& c) {
621 return c.mRule == RULE_EXCLUDE_UID && c.mValue.mUid == uid;
622 });
623 }
624 return NO_ERROR;
625 }
626
getDevicesForUid(uid_t uid,Vector<AudioDeviceTypeAddr> & devices) const627 status_t AudioPolicyMixCollection::getDevicesForUid(uid_t uid,
628 Vector<AudioDeviceTypeAddr>& devices) const {
629 // for each player mix: find rules that don't exclude this uid, and add the device to the list
630 for (size_t i = 0; i < size(); i++) {
631 bool ruleAllowsUid = true;
632 const AudioPolicyMix *mix = itemAt(i).get();
633 if (mix->mMixType != MIX_TYPE_PLAYERS) {
634 continue;
635 }
636 for (size_t j = 0; j < mix->mCriteria.size(); j++) {
637 const uint32_t rule = mix->mCriteria[j].mRule;
638 if (rule == RULE_EXCLUDE_UID
639 && uid == mix->mCriteria[j].mValue.mUid) {
640 ruleAllowsUid = false;
641 break;
642 }
643 }
644 if (ruleAllowsUid) {
645 devices.add(AudioDeviceTypeAddr(mix->mDeviceType, mix->mDeviceAddress.c_str()));
646 }
647 }
648 return NO_ERROR;
649 }
650
setUserIdDeviceAffinities(int userId,const AudioDeviceTypeAddrVector & devices)651 status_t AudioPolicyMixCollection::setUserIdDeviceAffinities(int userId,
652 const AudioDeviceTypeAddrVector& devices) {
653 // verify feasibility: for each player mix: if it already contains a
654 // "match userId" rule for this userId, return an error
655 // (adding a userId-device affinity would result in contradictory rules)
656 for (size_t i = 0; i < size(); i++) {
657 AudioPolicyMix* mix = itemAt(i).get();
658 if (!mix->isDeviceAffinityCompatible()) {
659 continue;
660 }
661 if (mix->hasUserIdRule(true /*match*/, userId)) {
662 return INVALID_OPERATION;
663 }
664 }
665
666 // remove existing rules for this userId
667 removeUserIdDeviceAffinities(userId);
668
669 // for each player mix:
670 // IF device is not a target for the mix,
671 // AND it doesn't have a "match userId" rule
672 // THEN add a rule to exclude the userId
673 for (size_t i = 0; i < size(); i++) {
674 AudioPolicyMix *mix = itemAt(i).get();
675 if (!mix->isDeviceAffinityCompatible()) {
676 continue;
677 }
678 // check if this mix goes to a device in the list of devices
679 bool deviceMatch = false;
680 const AudioDeviceTypeAddr mixDevice(mix->mDeviceType, mix->mDeviceAddress.c_str());
681 for (size_t j = 0; j < devices.size(); j++) {
682 if (mixDevice.equals(devices[j])) {
683 deviceMatch = true;
684 break;
685 }
686 }
687 if (!deviceMatch && !mix->hasUserIdRule(true /*match*/)) {
688 // this mix doesn't go to one of the listed devices for the given userId,
689 // and it's not already restricting the mix on a userId,
690 // modify its rules to exclude the userId
691 if (!mix->hasUserIdRule(false /* match */, userId)) {
692 // no need to do it again if userId is already excluded
693 mix->setExcludeUserId(userId);
694 mix->mRouteFlags = mix->mRouteFlags | MIX_ROUTE_FLAG_DISALLOWS_PREFERRED_DEVICE;
695 }
696 }
697 }
698
699 return NO_ERROR;
700 }
701
removeUserIdDeviceAffinities(int userId)702 status_t AudioPolicyMixCollection::removeUserIdDeviceAffinities(int userId) {
703 // for each player mix: remove existing rules that match or exclude this userId
704 for (size_t i = 0; i < size(); i++) {
705 AudioPolicyMix *mix = itemAt(i).get();
706 if (!mix->isDeviceAffinityCompatible()) {
707 continue;
708 }
709
710 // is this rule excluding the userId? (not considering userId match rules
711 // as those are not used for userId-device affinity)
712 EraseCriteriaIf(mix->mCriteria, [userId](const AudioMixMatchCriterion& c) {
713 return c.mRule == RULE_EXCLUDE_USERID && c.mValue.mUserId == userId;
714 });
715
716 if (!mix->hasUserIdRule(false /* match */)) {
717 mix->mRouteFlags = mix->mRouteFlags & ~MIX_ROUTE_FLAG_DISALLOWS_PREFERRED_DEVICE;
718 }
719 }
720 return NO_ERROR;
721 }
722
getDevicesForUserId(int userId,AudioDeviceTypeAddrVector & devices) const723 status_t AudioPolicyMixCollection::getDevicesForUserId(int userId,
724 AudioDeviceTypeAddrVector& devices) const {
725 // for each player mix:
726 // find rules that don't exclude this userId, and add the device to the list
727 for (size_t i = 0; i < size(); i++) {
728 bool ruleAllowsUserId = true;
729 const AudioPolicyMix *mix = itemAt(i).get();
730 if (mix->mMixType != MIX_TYPE_PLAYERS) {
731 continue;
732 }
733 for (size_t j = 0; j < mix->mCriteria.size(); j++) {
734 const uint32_t rule = mix->mCriteria[j].mRule;
735 if (rule == RULE_EXCLUDE_USERID
736 && userId == mix->mCriteria[j].mValue.mUserId) {
737 ruleAllowsUserId = false;
738 break;
739 }
740 }
741 if (ruleAllowsUserId) {
742 devices.push_back(AudioDeviceTypeAddr(mix->mDeviceType, mix->mDeviceAddress.c_str()));
743 }
744 }
745 return NO_ERROR;
746 }
747
dump(String8 * dst) const748 void AudioPolicyMixCollection::dump(String8 *dst) const
749 {
750 dst->append("\n Audio Policy Mix:\n");
751 for (size_t i = 0; i < size(); i++) {
752 itemAt(i)->dump(dst, 2, i);
753 }
754 }
755
extractAddressFromAudioAttributes(const audio_attributes_t & attr)756 std::optional<std::string> extractAddressFromAudioAttributes(const audio_attributes_t& attr) {
757 static const std::regex addrTagRegex("addr=([^;]+)");
758
759 std::cmatch match;
760 if (std::regex_search(attr.tags, match, addrTagRegex)) {
761 return match[1].str();
762 }
763 return std::nullopt;
764 }
765
766 }; //namespace android
767