xref: /aosp_15_r20/frameworks/av/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp (revision ec779b8e0859a360c3d303172224686826e6e0e1)
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, flags, 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_output_flags_t outputFlags,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_output_flags_t outputFlags,
426     const audio_config_base_t& config, 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 format on direct output.
455         if (!is_mix_loopback(mix->mRouteFlags) &&
456                 config.format != AUDIO_CONFIG_BASE_INITIALIZER.format) {
457             if (!audio_output_is_mixed_output_flags(outputFlags)) {
458                 // Direct output must match format exactly.
459                 if (config.format != mix->mFormat.format) return false;
460             } else {
461                 // If mixable, both requested and mix format must be linear pcm.
462                 if (!audio_is_linear_pcm(config.format) ||
463                           !audio_is_linear_pcm(mix->mFormat.format)) return false;
464             }
465         }
466 
467         // if there is an address match, prioritize that match
468         if (matchAddressToTags(attributes, mix->mDeviceAddress)
469             || areMixCriteriaMatched(mix->mCriteria, attributes, uid, session)) {
470                 ALOGV("\tgetOutputForAttr will use mix %zu", mixIndex);
471                 return true;
472         }
473     } else if (mix->mMixType == MIX_TYPE_RECORDERS) {
474         if (attributes.usage == AUDIO_USAGE_VIRTUAL_SOURCE &&
475             matchAddressToTags(attributes, mix->mDeviceAddress)) {
476             return true;
477         }
478     }
479     return false;
480 }
481 
getDeviceAndMixForOutput(const sp<SwAudioOutputDescriptor> & output,const DeviceVector & availableOutputDevices)482 sp<DeviceDescriptor> AudioPolicyMixCollection::getDeviceAndMixForOutput(
483         const sp<SwAudioOutputDescriptor> &output,
484         const DeviceVector &availableOutputDevices)
485 {
486     for (size_t i = 0; i < size(); i++) {
487         if (itemAt(i)->getOutput() == output) {
488             // This Desc is involved in a Mix, which has the highest prio
489             return getOutputDeviceForMix(itemAt(i).get(), availableOutputDevices);
490         }
491     }
492     return nullptr;
493 }
494 
getDeviceAndMixForInputSource(const audio_attributes_t & attributes,const DeviceVector & availDevices,uid_t uid,audio_session_t session,sp<AudioPolicyMix> * policyMix) const495 sp<DeviceDescriptor> AudioPolicyMixCollection::getDeviceAndMixForInputSource(
496         const audio_attributes_t& attributes,
497         const DeviceVector &availDevices,
498         uid_t uid,
499         audio_session_t session,
500         sp<AudioPolicyMix> *policyMix) const
501 {
502     for (size_t i = 0; i < size(); i++) {
503         AudioPolicyMix *mix = itemAt(i).get();
504         if (mix->mMixType != MIX_TYPE_RECORDERS) {
505             continue;
506         }
507         if (areMixCriteriaMatched(mix->mCriteria, attributes, uid, session)) {
508             // Assuming PolicyMix only for remote submix for input
509             // so mix->mDeviceType can only be AUDIO_DEVICE_OUT_REMOTE_SUBMIX.
510             auto mixDevice = availDevices.getDevice(AUDIO_DEVICE_IN_REMOTE_SUBMIX,
511              mix->mDeviceAddress, AUDIO_FORMAT_DEFAULT);
512                 if (mixDevice != nullptr) {
513                     if (policyMix != nullptr) {
514                         *policyMix = mix;
515                     }
516                     return mixDevice;
517                 }
518         }
519     }
520     return nullptr;
521 }
522 
getInputMixForAttr(audio_attributes_t attr,sp<AudioPolicyMix> * policyMix)523 status_t AudioPolicyMixCollection::getInputMixForAttr(
524         audio_attributes_t attr, sp<AudioPolicyMix> *policyMix)
525 {
526     std::optional<std::string> address = extractAddressFromAudioAttributes(attr);
527     if (!address.has_value()) {
528         return BAD_VALUE;
529     }
530 
531 #ifdef LOG_NDEBUG
532     ALOGV("getInputMixForAttr looking for address %s for source %d\n  mixes available:",
533             address->c_str(), attr.source);
534     for (size_t i = 0; i < size(); i++) {
535         const sp<AudioPolicyMix> audioPolicyMix = itemAt(i);
536         ALOGV("\tmix %zu address=%s", i, audioPolicyMix->mDeviceAddress.c_str());
537     }
538 #endif
539 
540     size_t index;
541     for (index = 0; index < size(); index++) {
542         const sp<AudioPolicyMix>& registeredMix = itemAt(index);
543         if (address->compare(registeredMix->mDeviceAddress.c_str()) == 0) {
544             ALOGD("getInputMixForAttr found addr=%s dev=0x%x",
545                     registeredMix->mDeviceAddress.c_str(), registeredMix->mDeviceType);
546             break;
547         }
548     }
549     if (index == size()) {
550         ALOGW("getInputMixForAttr() no policy for address %s", address->c_str());
551         return BAD_VALUE;
552     }
553     const sp<AudioPolicyMix> audioPolicyMix = itemAt(index);
554 
555     if (audioPolicyMix->mMixType != MIX_TYPE_PLAYERS) {
556         ALOGW("getInputMixForAttr() bad policy mix type for address %s", address->c_str());
557         return BAD_VALUE;
558     }
559     if (policyMix != nullptr) {
560         *policyMix = audioPolicyMix;
561     }
562     return NO_ERROR;
563 }
564 
setUidDeviceAffinities(uid_t uid,const AudioDeviceTypeAddrVector & devices)565 status_t AudioPolicyMixCollection::setUidDeviceAffinities(uid_t uid,
566         const AudioDeviceTypeAddrVector& devices) {
567     // verify feasibility: for each player mix: if it already contains a
568     //    "match uid" rule for this uid, return an error
569     //    (adding a uid-device affinity would result in contradictory rules)
570     for (size_t i = 0; i < size(); i++) {
571         const AudioPolicyMix* mix = itemAt(i).get();
572         if (!mix->isDeviceAffinityCompatible()) {
573             continue;
574         }
575         if (mix->hasUidRule(true /*match*/, uid)) {
576             return INVALID_OPERATION;
577         }
578     }
579 
580     // remove existing rules for this uid
581     removeUidDeviceAffinities(uid);
582 
583     // for each player mix:
584     //   IF    device is not a target for the mix,
585     //     AND it doesn't have a "match uid" rule
586     //   THEN add a rule to exclude the uid
587     for (size_t i = 0; i < size(); i++) {
588         AudioPolicyMix *mix = itemAt(i).get();
589         if (!mix->isDeviceAffinityCompatible()) {
590             continue;
591         }
592         // check if this mix goes to a device in the list of devices
593         bool deviceMatch = false;
594         const AudioDeviceTypeAddr mixDevice(mix->mDeviceType, mix->mDeviceAddress.c_str());
595         for (size_t j = 0; j < devices.size(); j++) {
596             if (mixDevice.equals(devices[j])) {
597                 deviceMatch = true;
598                 break;
599             }
600         }
601         if (!deviceMatch && !mix->hasMatchUidRule()) {
602             // this mix doesn't go to one of the listed devices for the given uid,
603             // and it's not already restricting the mix on a uid,
604             // modify its rules to exclude the uid
605             if (!mix->hasUidRule(false /*match*/, uid)) {
606                 // no need to do it again if uid is already excluded
607                 mix->setExcludeUid(uid);
608             }
609         }
610     }
611 
612     return NO_ERROR;
613 }
614 
removeUidDeviceAffinities(uid_t uid)615 status_t AudioPolicyMixCollection::removeUidDeviceAffinities(uid_t uid) {
616     // for each player mix: remove existing rules that match or exclude this uid
617     for (size_t i = 0; i < size(); i++) {
618         AudioPolicyMix *mix = itemAt(i).get();
619         if (!mix->isDeviceAffinityCompatible()) {
620             continue;
621         }
622 
623         // is this rule excluding the uid? (not considering uid match rules
624         // as those are not used for uid-device affinity)
625         EraseCriteriaIf(mix->mCriteria, [uid](const AudioMixMatchCriterion& c) {
626             return c.mRule == RULE_EXCLUDE_UID && c.mValue.mUid == uid;
627         });
628     }
629     return NO_ERROR;
630 }
631 
getDevicesForUid(uid_t uid,Vector<AudioDeviceTypeAddr> & devices) const632 status_t AudioPolicyMixCollection::getDevicesForUid(uid_t uid,
633         Vector<AudioDeviceTypeAddr>& devices) const {
634     // for each player mix: find rules that don't exclude this uid, and add the device to the list
635     for (size_t i = 0; i < size(); i++) {
636         bool ruleAllowsUid = true;
637         const AudioPolicyMix *mix = itemAt(i).get();
638         if (mix->mMixType != MIX_TYPE_PLAYERS) {
639             continue;
640         }
641         for (size_t j = 0; j < mix->mCriteria.size(); j++) {
642             const uint32_t rule = mix->mCriteria[j].mRule;
643             if (rule == RULE_EXCLUDE_UID
644                     && uid == mix->mCriteria[j].mValue.mUid) {
645                 ruleAllowsUid = false;
646                 break;
647             }
648         }
649         if (ruleAllowsUid) {
650             devices.add(AudioDeviceTypeAddr(mix->mDeviceType, mix->mDeviceAddress.c_str()));
651         }
652     }
653     return NO_ERROR;
654 }
655 
setUserIdDeviceAffinities(int userId,const AudioDeviceTypeAddrVector & devices)656 status_t AudioPolicyMixCollection::setUserIdDeviceAffinities(int userId,
657         const AudioDeviceTypeAddrVector& devices) {
658     // verify feasibility: for each player mix: if it already contains a
659     //    "match userId" rule for this userId, return an error
660     //    (adding a userId-device affinity would result in contradictory rules)
661     for (size_t i = 0; i < size(); i++) {
662         AudioPolicyMix* mix = itemAt(i).get();
663         if (!mix->isDeviceAffinityCompatible()) {
664             continue;
665         }
666         if (mix->hasUserIdRule(true /*match*/, userId)) {
667             return INVALID_OPERATION;
668         }
669     }
670 
671     // remove existing rules for this userId
672     removeUserIdDeviceAffinities(userId);
673 
674     // for each player mix:
675     //   IF    device is not a target for the mix,
676     //     AND it doesn't have a "match userId" rule
677     //   THEN add a rule to exclude the userId
678     for (size_t i = 0; i < size(); i++) {
679         AudioPolicyMix *mix = itemAt(i).get();
680         if (!mix->isDeviceAffinityCompatible()) {
681             continue;
682         }
683         // check if this mix goes to a device in the list of devices
684         bool deviceMatch = false;
685         const AudioDeviceTypeAddr mixDevice(mix->mDeviceType, mix->mDeviceAddress.c_str());
686         for (size_t j = 0; j < devices.size(); j++) {
687             if (mixDevice.equals(devices[j])) {
688                 deviceMatch = true;
689                 break;
690             }
691         }
692         if (!deviceMatch && !mix->hasUserIdRule(true /*match*/)) {
693             // this mix doesn't go to one of the listed devices for the given userId,
694             // and it's not already restricting the mix on a userId,
695             // modify its rules to exclude the userId
696             if (!mix->hasUserIdRule(false /* match */, userId)) {
697                 // no need to do it again if userId is already excluded
698                 mix->setExcludeUserId(userId);
699                 mix->mRouteFlags = mix->mRouteFlags | MIX_ROUTE_FLAG_DISALLOWS_PREFERRED_DEVICE;
700             }
701         }
702     }
703 
704     return NO_ERROR;
705 }
706 
removeUserIdDeviceAffinities(int userId)707 status_t AudioPolicyMixCollection::removeUserIdDeviceAffinities(int userId) {
708     // for each player mix: remove existing rules that match or exclude this userId
709     for (size_t i = 0; i < size(); i++) {
710         AudioPolicyMix *mix = itemAt(i).get();
711         if (!mix->isDeviceAffinityCompatible()) {
712             continue;
713         }
714 
715         // is this rule excluding the userId? (not considering userId match rules
716         // as those are not used for userId-device affinity)
717         EraseCriteriaIf(mix->mCriteria, [userId](const AudioMixMatchCriterion& c) {
718             return c.mRule == RULE_EXCLUDE_USERID && c.mValue.mUserId == userId;
719         });
720 
721         if (!mix->hasUserIdRule(false /* match */)) {
722             mix->mRouteFlags = mix->mRouteFlags & ~MIX_ROUTE_FLAG_DISALLOWS_PREFERRED_DEVICE;
723         }
724     }
725     return NO_ERROR;
726 }
727 
getDevicesForUserId(int userId,AudioDeviceTypeAddrVector & devices) const728 status_t AudioPolicyMixCollection::getDevicesForUserId(int userId,
729         AudioDeviceTypeAddrVector& devices) const {
730     // for each player mix:
731     // find rules that don't exclude this userId, and add the device to the list
732     for (size_t i = 0; i < size(); i++) {
733         bool ruleAllowsUserId = true;
734         const AudioPolicyMix *mix = itemAt(i).get();
735         if (mix->mMixType != MIX_TYPE_PLAYERS) {
736             continue;
737         }
738         for (size_t j = 0; j < mix->mCriteria.size(); j++) {
739             const uint32_t rule = mix->mCriteria[j].mRule;
740             if (rule == RULE_EXCLUDE_USERID
741                     && userId == mix->mCriteria[j].mValue.mUserId) {
742                 ruleAllowsUserId = false;
743                 break;
744             }
745         }
746         if (ruleAllowsUserId) {
747             devices.push_back(AudioDeviceTypeAddr(mix->mDeviceType, mix->mDeviceAddress.c_str()));
748         }
749     }
750     return NO_ERROR;
751 }
752 
dump(String8 * dst) const753 void AudioPolicyMixCollection::dump(String8 *dst) const
754 {
755     dst->append("\n Audio Policy Mix:\n");
756     for (size_t i = 0; i < size(); i++) {
757         itemAt(i)->dump(dst, 2, i);
758     }
759 }
760 
extractAddressFromAudioAttributes(const audio_attributes_t & attr)761 std::optional<std::string> extractAddressFromAudioAttributes(const audio_attributes_t& attr) {
762     static const std::regex addrTagRegex("addr=([^;]+)");
763 
764     std::cmatch match;
765     if (std::regex_search(attr.tags, match, addrTagRegex)) {
766         return match[1].str();
767     }
768     return std::nullopt;
769 }
770 
771 }; //namespace android
772