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