• 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 <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