• 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 "AudioPolicyMix.h"
21 #include "TypeConverter.h"
22 #include "HwModule.h"
23 #include "AudioPort.h"
24 #include "IOProfile.h"
25 #include "AudioGain.h"
26 #include <AudioOutputDescriptor.h>
27 
28 namespace android {
29 
dump(String8 * dst,int spaces,int index) const30 void AudioPolicyMix::dump(String8 *dst, int spaces, int index) const
31 {
32     dst->appendFormat("%*sAudio Policy Mix %d:\n", spaces, "", index + 1);
33     std::string mixTypeLiteral;
34     if (!MixTypeConverter::toString(mMixType, mixTypeLiteral)) {
35         ALOGE("%s: failed to convert mix type %d", __FUNCTION__, mMixType);
36         return;
37     }
38     dst->appendFormat("%*s- mix type: %s\n", spaces, "", mixTypeLiteral.c_str());
39 
40     std::string routeFlagLiteral;
41     RouteFlagTypeConverter::maskToString(mRouteFlags, routeFlagLiteral);
42     dst->appendFormat("%*s- Route Flags: %s\n", spaces, "", routeFlagLiteral.c_str());
43 
44     dst->appendFormat("%*s- device type: %s\n", spaces, "", toString(mDeviceType).c_str());
45 
46     dst->appendFormat("%*s- device address: %s\n", spaces, "", mDeviceAddress.string());
47 
48     int indexCriterion = 0;
49     for (const auto &criterion : mCriteria) {
50         dst->appendFormat("%*s- Criterion %d: ", spaces + 2, "", indexCriterion++);
51 
52         std::string ruleType, ruleValue;
53         bool unknownRule = !RuleTypeConverter::toString(criterion.mRule, ruleType);
54         switch (criterion.mRule & ~RULE_EXCLUSION_MASK) { // no need to match RULE_EXCLUDE_...
55         case RULE_MATCH_ATTRIBUTE_USAGE:
56             UsageTypeConverter::toString(criterion.mValue.mUsage, ruleValue);
57             break;
58         case RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET:
59             SourceTypeConverter::toString(criterion.mValue.mSource, ruleValue);
60             break;
61         case RULE_MATCH_UID:
62             ruleValue = std::to_string(criterion.mValue.mUid);
63             break;
64         default:
65             unknownRule = true;
66         }
67 
68         if (!unknownRule) {
69             dst->appendFormat("%s %s\n", ruleType.c_str(), ruleValue.c_str());
70         } else {
71             dst->appendFormat("Unknown rule type value 0x%x\n", criterion.mRule);
72         }
73     }
74 }
75 
registerMix(AudioMix mix,sp<SwAudioOutputDescriptor> desc)76 status_t AudioPolicyMixCollection::registerMix(AudioMix mix, sp<SwAudioOutputDescriptor> desc)
77 {
78     for (size_t i = 0; i < size(); i++) {
79         const sp<AudioPolicyMix>& registeredMix = itemAt(i);
80         if (mix.mDeviceType == registeredMix->mDeviceType
81                 && mix.mDeviceAddress.compare(registeredMix->mDeviceAddress) == 0) {
82             ALOGE("registerMix(): mix already registered for dev=0x%x addr=%s",
83                     mix.mDeviceType, mix.mDeviceAddress.string());
84             return BAD_VALUE;
85         }
86     }
87     sp<AudioPolicyMix> policyMix = new AudioPolicyMix(mix);
88     add(policyMix);
89     ALOGD("registerMix(): adding mix for dev=0x%x addr=%s",
90             policyMix->mDeviceType, policyMix->mDeviceAddress.string());
91 
92     if (desc != 0) {
93         desc->mPolicyMix = policyMix;
94         policyMix->setOutput(desc);
95     }
96     return NO_ERROR;
97 }
98 
unregisterMix(const AudioMix & mix)99 status_t AudioPolicyMixCollection::unregisterMix(const AudioMix& mix)
100 {
101     for (size_t i = 0; i < size(); i++) {
102         const sp<AudioPolicyMix>& registeredMix = itemAt(i);
103         if (mix.mDeviceType == registeredMix->mDeviceType
104                 && mix.mDeviceAddress.compare(registeredMix->mDeviceAddress) == 0) {
105             ALOGD("unregisterMix(): removing mix for dev=0x%x addr=%s",
106                     mix.mDeviceType, mix.mDeviceAddress.string());
107             removeAt(i);
108             return NO_ERROR;
109         }
110     }
111 
112     ALOGE("unregisterMix(): mix not registered for dev=0x%x addr=%s",
113             mix.mDeviceType, mix.mDeviceAddress.string());
114     return BAD_VALUE;
115 }
116 
getAudioPolicyMix(audio_devices_t deviceType,const String8 & address,sp<AudioPolicyMix> & policyMix) const117 status_t AudioPolicyMixCollection::getAudioPolicyMix(audio_devices_t deviceType,
118         const String8& address, sp<AudioPolicyMix> &policyMix) const
119 {
120 
121     ALOGV("getAudioPolicyMix() for dev=0x%x addr=%s", deviceType, address.string());
122     for (ssize_t i = 0; i < size(); i++) {
123         // Workaround: when an in audio policy is registered, it opens an output
124         // that tries to find the audio policy, thus the device must be ignored.
125         if (itemAt(i)->mDeviceAddress.compare(address) == 0) {
126             policyMix = itemAt(i);
127             ALOGV("getAudioPolicyMix: found mix %zu match (devType=0x%x addr=%s)",
128                     i, deviceType, address.string());
129             return NO_ERROR;
130         }
131     }
132 
133     ALOGE("getAudioPolicyMix(): mix not registered for dev=0x%x addr=%s",
134             deviceType, address.string());
135     return BAD_VALUE;
136 }
137 
closeOutput(sp<SwAudioOutputDescriptor> & desc)138 void AudioPolicyMixCollection::closeOutput(sp<SwAudioOutputDescriptor> &desc)
139 {
140     for (size_t i = 0; i < size(); i++) {
141         sp<AudioPolicyMix> policyMix = itemAt(i);
142         if (policyMix->getOutput() == desc) {
143             policyMix->clearOutput();
144         }
145     }
146 }
147 
getOutputForAttr(const audio_attributes_t & attributes,uid_t uid,audio_output_flags_t flags,sp<SwAudioOutputDescriptor> & primaryDesc,std::vector<sp<SwAudioOutputDescriptor>> * secondaryDescs)148 status_t AudioPolicyMixCollection::getOutputForAttr(
149         const audio_attributes_t& attributes, uid_t uid,
150         audio_output_flags_t flags,
151         sp<SwAudioOutputDescriptor> &primaryDesc,
152         std::vector<sp<SwAudioOutputDescriptor>> *secondaryDescs)
153 {
154     ALOGV("getOutputForAttr() querying %zu mixes:", size());
155     primaryDesc = 0;
156     for (size_t i = 0; i < size(); i++) {
157         sp<AudioPolicyMix> policyMix = itemAt(i);
158         const bool primaryOutputMix = !is_mix_loopback_render(policyMix->mRouteFlags);
159         if (!primaryOutputMix && (flags & AUDIO_OUTPUT_FLAG_MMAP_NOIRQ)) {
160             // AAudio does not support MMAP_NO_IRQ loopback render, and there is no way with
161             // the current MmapStreamInterface::start to reject a specific client added to a shared
162             // mmap stream.
163             // As a result all MMAP_NOIRQ requests have to be rejected when an loopback render
164             // policy is present. That ensures no shared mmap stream is used when an loopback
165             // render policy is registered.
166             ALOGD("%s: Rejecting MMAP_NOIRQ request due to LOOPBACK|RENDER mix present.", __func__);
167             return INVALID_OPERATION;
168         }
169 
170         sp<SwAudioOutputDescriptor> policyDesc = policyMix->getOutput();
171         if (!policyDesc) {
172             ALOGV("%s: Skiping %zu: Mix has no output", __func__, i);
173             continue;
174         }
175 
176         if (primaryOutputMix && primaryDesc != 0) {
177             ALOGV("%s: Skiping %zu: Primary output already found", __func__, i);
178             continue; // Primary output already found
179         }
180 
181         switch (mixMatch(policyMix.get(), i, attributes, uid)) {
182             case MixMatchStatus::INVALID_MIX:
183                 // The mix has contradictory rules, ignore it
184                 // TODO: reject invalid mix at registration
185                 continue;
186             case MixMatchStatus::NO_MATCH:
187                 ALOGV("%s: Mix %zu: does not match", __func__, i);
188                 continue; // skip the mix
189             case MixMatchStatus::MATCH:;
190         }
191 
192         policyDesc->mPolicyMix = policyMix;
193         if (primaryOutputMix) {
194             primaryDesc = policyDesc;
195             ALOGV("%s: Mix %zu: set primary desc", __func__, i);
196         } else {
197             if (policyDesc->mIoHandle == AUDIO_IO_HANDLE_NONE) {
198                 ALOGV("%s: Mix %zu ignored as secondaryOutput because not opened yet", __func__, i);
199             } else {
200                 ALOGV("%s: Add a secondary desc %zu", __func__, i);
201                 secondaryDescs->push_back(policyDesc);
202             }
203         }
204     }
205     return NO_ERROR;
206 }
207 
mixMatch(const AudioMix * mix,size_t mixIndex,const audio_attributes_t & attributes,uid_t uid)208 AudioPolicyMixCollection::MixMatchStatus AudioPolicyMixCollection::mixMatch(
209         const AudioMix* mix, size_t mixIndex, const audio_attributes_t& attributes, uid_t uid) {
210 
211     if (mix->mMixType == MIX_TYPE_PLAYERS) {
212         // Loopback render mixes are created from a public API and thus restricted
213         // to non sensible audio that have not opted out.
214         if (is_mix_loopback_render(mix->mRouteFlags)) {
215             auto hasFlag = [](auto flags, auto flag) { return (flags & flag) == flag; };
216             if (hasFlag(attributes.flags, AUDIO_FLAG_NO_SYSTEM_CAPTURE)) {
217                 return MixMatchStatus::NO_MATCH;
218             }
219             if (!mix->mAllowPrivilegedPlaybackCapture &&
220                 hasFlag(attributes.flags, AUDIO_FLAG_NO_MEDIA_PROJECTION)) {
221                 return MixMatchStatus::NO_MATCH;
222             }
223             if (!(attributes.usage == AUDIO_USAGE_UNKNOWN ||
224                   attributes.usage == AUDIO_USAGE_MEDIA ||
225                   attributes.usage == AUDIO_USAGE_GAME)) {
226                 return MixMatchStatus::NO_MATCH;
227             }
228         }
229         // TODO if adding more player rules (currently only 2), make rule handling "generic"
230         //      as there is no difference in the treatment of usage- or uid-based rules
231         bool hasUsageMatchRules = false;
232         bool hasUsageExcludeRules = false;
233         bool usageMatchFound = false;
234         bool usageExclusionFound = false;
235 
236         bool hasUidMatchRules = false;
237         bool hasUidExcludeRules = false;
238         bool uidMatchFound = false;
239         bool uidExclusionFound = false;
240 
241         bool hasAddrMatch = false;
242 
243         // iterate over all mix criteria to list what rules this mix contains
244         for (size_t j = 0; j < mix->mCriteria.size(); j++) {
245             ALOGV(" getOutputForAttr: mix %zu: inspecting mix criteria %zu of %zu",
246                     mixIndex, j, mix->mCriteria.size());
247 
248             // if there is an address match, prioritize that match
249             if (strncmp(attributes.tags, "addr=", strlen("addr=")) == 0 &&
250                     strncmp(attributes.tags + strlen("addr="),
251                             mix->mDeviceAddress.string(),
252                             AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - strlen("addr=") - 1) == 0) {
253                 hasAddrMatch = true;
254                 break;
255             }
256 
257             switch (mix->mCriteria[j].mRule) {
258             case RULE_MATCH_ATTRIBUTE_USAGE:
259                 ALOGV("\tmix has RULE_MATCH_ATTRIBUTE_USAGE for usage %d",
260                                             mix->mCriteria[j].mValue.mUsage);
261                 hasUsageMatchRules = true;
262                 if (mix->mCriteria[j].mValue.mUsage == attributes.usage) {
263                     // found one match against all allowed usages
264                     usageMatchFound = true;
265                 }
266                 break;
267             case RULE_EXCLUDE_ATTRIBUTE_USAGE:
268                 ALOGV("\tmix has RULE_EXCLUDE_ATTRIBUTE_USAGE for usage %d",
269                         mix->mCriteria[j].mValue.mUsage);
270                 hasUsageExcludeRules = true;
271                 if (mix->mCriteria[j].mValue.mUsage == attributes.usage) {
272                     // found this usage is to be excluded
273                     usageExclusionFound = true;
274                 }
275                 break;
276             case RULE_MATCH_UID:
277                 ALOGV("\tmix has RULE_MATCH_UID for uid %d", mix->mCriteria[j].mValue.mUid);
278                 hasUidMatchRules = true;
279                 if (mix->mCriteria[j].mValue.mUid == uid) {
280                     // found one UID match against all allowed UIDs
281                     uidMatchFound = true;
282                 }
283                 break;
284             case RULE_EXCLUDE_UID:
285                 ALOGV("\tmix has RULE_EXCLUDE_UID for uid %d", mix->mCriteria[j].mValue.mUid);
286                 hasUidExcludeRules = true;
287                 if (mix->mCriteria[j].mValue.mUid == uid) {
288                     // found this UID is to be excluded
289                     uidExclusionFound = true;
290                 }
291                 break;
292             default:
293                 break;
294             }
295 
296             // consistency checks: for each "dimension" of rules (usage, uid...), we can
297             // only have MATCH rules, or EXCLUDE rules in each dimension, not a combination
298             if (hasUsageMatchRules && hasUsageExcludeRules) {
299                 ALOGE("getOutputForAttr: invalid combination of RULE_MATCH_ATTRIBUTE_USAGE"
300                         " and RULE_EXCLUDE_ATTRIBUTE_USAGE in mix %zu", mixIndex);
301                 return MixMatchStatus::INVALID_MIX;
302             }
303             if (hasUidMatchRules && hasUidExcludeRules) {
304                 ALOGE("getOutputForAttr: invalid combination of RULE_MATCH_UID"
305                         " and RULE_EXCLUDE_UID in mix %zu", mixIndex);
306                 return MixMatchStatus::INVALID_MIX;
307             }
308 
309             if ((hasUsageExcludeRules && usageExclusionFound)
310                     || (hasUidExcludeRules && uidExclusionFound)) {
311                 break; // stop iterating on criteria because an exclusion was found (will fail)
312             }
313 
314         }//iterate on mix criteria
315 
316         // determine if exiting on success (or implicit failure as desc is 0)
317         if (hasAddrMatch ||
318                 !((hasUsageExcludeRules && usageExclusionFound) ||
319                   (hasUsageMatchRules && !usageMatchFound)  ||
320                   (hasUidExcludeRules && uidExclusionFound) ||
321                   (hasUidMatchRules && !uidMatchFound))) {
322             ALOGV("\tgetOutputForAttr will use mix %zu", mixIndex);
323             return MixMatchStatus::MATCH;
324         }
325 
326     } else if (mix->mMixType == MIX_TYPE_RECORDERS) {
327         if (attributes.usage == AUDIO_USAGE_VIRTUAL_SOURCE &&
328                 strncmp(attributes.tags, "addr=", strlen("addr=")) == 0 &&
329                 strncmp(attributes.tags + strlen("addr="),
330                         mix->mDeviceAddress.string(),
331                         AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - strlen("addr=") - 1) == 0) {
332             return MixMatchStatus::MATCH;
333         }
334     }
335     return MixMatchStatus::NO_MATCH;
336 }
337 
getDeviceAndMixForOutput(const sp<SwAudioOutputDescriptor> & output,const DeviceVector & availableOutputDevices)338 sp<DeviceDescriptor> AudioPolicyMixCollection::getDeviceAndMixForOutput(
339         const sp<SwAudioOutputDescriptor> &output,
340         const DeviceVector &availableOutputDevices)
341 {
342     for (size_t i = 0; i < size(); i++) {
343         if (itemAt(i)->getOutput() == output) {
344             // This Desc is involved in a Mix, which has the highest prio
345             audio_devices_t deviceType = itemAt(i)->mDeviceType;
346             String8 address = itemAt(i)->mDeviceAddress;
347             ALOGV("%s: device (0x%x, addr=%s) forced by mix",
348                   __FUNCTION__, deviceType, address.c_str());
349             return availableOutputDevices.getDevice(deviceType, address, AUDIO_FORMAT_DEFAULT);
350         }
351     }
352     return nullptr;
353 }
354 
getDeviceAndMixForInputSource(audio_source_t inputSource,const DeviceVector & availDevices,sp<AudioPolicyMix> * policyMix) const355 sp<DeviceDescriptor> AudioPolicyMixCollection::getDeviceAndMixForInputSource(
356         audio_source_t inputSource,
357         const DeviceVector &availDevices,
358         sp<AudioPolicyMix> *policyMix) const
359 {
360     for (size_t i = 0; i < size(); i++) {
361         AudioPolicyMix *mix = itemAt(i).get();
362         if (mix->mMixType != MIX_TYPE_RECORDERS) {
363             continue;
364         }
365         for (size_t j = 0; j < mix->mCriteria.size(); j++) {
366             if ((RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET == mix->mCriteria[j].mRule &&
367                     mix->mCriteria[j].mValue.mSource == inputSource) ||
368                (RULE_EXCLUDE_ATTRIBUTE_CAPTURE_PRESET == mix->mCriteria[j].mRule &&
369                     mix->mCriteria[j].mValue.mSource != inputSource)) {
370                 // assuming PolicyMix only for remote submix for input
371                 // so mix->mDeviceType can only be AUDIO_DEVICE_OUT_REMOTE_SUBMIX
372                 audio_devices_t device = AUDIO_DEVICE_IN_REMOTE_SUBMIX;
373                 auto mixDevice =
374                         availDevices.getDevice(device, mix->mDeviceAddress, AUDIO_FORMAT_DEFAULT);
375                 if (mixDevice != nullptr) {
376                     if (policyMix != nullptr) {
377                         *policyMix = mix;
378                     }
379                     return mixDevice;
380                 }
381                 break;
382             }
383         }
384     }
385     return nullptr;
386 }
387 
getInputMixForAttr(audio_attributes_t attr,sp<AudioPolicyMix> * policyMix)388 status_t AudioPolicyMixCollection::getInputMixForAttr(
389         audio_attributes_t attr, sp<AudioPolicyMix> *policyMix)
390 {
391     if (strncmp(attr.tags, "addr=", strlen("addr=")) != 0) {
392         return BAD_VALUE;
393     }
394     String8 address(attr.tags + strlen("addr="));
395 
396 #ifdef LOG_NDEBUG
397     ALOGV("getInputMixForAttr looking for address %s for source %d\n  mixes available:",
398             address.string(), attr.source);
399     for (size_t i = 0; i < size(); i++) {
400         const sp<AudioPolicyMix> audioPolicyMix = itemAt(i);
401         ALOGV("\tmix %zu address=%s", i, audioPolicyMix->mDeviceAddress.string());
402     }
403 #endif
404 
405     size_t index;
406     for (index = 0; index < size(); index++) {
407         const sp<AudioPolicyMix>& registeredMix = itemAt(index);
408         if (registeredMix->mDeviceAddress.compare(address) == 0) {
409             ALOGD("getInputMixForAttr found addr=%s dev=0x%x",
410                     registeredMix->mDeviceAddress.string(), registeredMix->mDeviceType);
411             break;
412         }
413     }
414     if (index == size()) {
415         ALOGW("getInputMixForAttr() no policy for address %s", address.string());
416         return BAD_VALUE;
417     }
418     const sp<AudioPolicyMix> audioPolicyMix = itemAt(index);
419 
420     if (audioPolicyMix->mMixType != MIX_TYPE_PLAYERS) {
421         ALOGW("getInputMixForAttr() bad policy mix type for address %s", address.string());
422         return BAD_VALUE;
423     }
424     if (policyMix != nullptr) {
425         *policyMix = audioPolicyMix;
426     }
427     return NO_ERROR;
428 }
429 
setUidDeviceAffinities(uid_t uid,const Vector<AudioDeviceTypeAddr> & devices)430 status_t AudioPolicyMixCollection::setUidDeviceAffinities(uid_t uid,
431         const Vector<AudioDeviceTypeAddr>& devices) {
432     // verify feasibility: for each player mix: if it already contains a
433     //    "match uid" rule for this uid, return an error
434     //    (adding a uid-device affinity would result in contradictory rules)
435     for (size_t i = 0; i < size(); i++) {
436         const AudioPolicyMix* mix = itemAt(i).get();
437         if (!mix->isDeviceAffinityCompatible()) {
438             continue;
439         }
440         if (mix->hasUidRule(true /*match*/, uid)) {
441             return INVALID_OPERATION;
442         }
443     }
444 
445     // remove existing rules for this uid
446     removeUidDeviceAffinities(uid);
447 
448     // for each player mix:
449     //   IF    device is not a target for the mix,
450     //     AND it doesn't have a "match uid" rule
451     //   THEN add a rule to exclude the uid
452     for (size_t i = 0; i < size(); i++) {
453         const AudioPolicyMix *mix = itemAt(i).get();
454         if (!mix->isDeviceAffinityCompatible()) {
455             continue;
456         }
457         // check if this mix goes to a device in the list of devices
458         bool deviceMatch = false;
459         for (size_t j = 0; j < devices.size(); j++) {
460             if (devices[j].mType == mix->mDeviceType
461                     && devices[j].mAddress == mix->mDeviceAddress) {
462                 deviceMatch = true;
463                 break;
464             }
465         }
466         if (!deviceMatch && !mix->hasMatchUidRule()) {
467             // this mix doesn't go to one of the listed devices for the given uid,
468             // and it's not already restricting the mix on a uid,
469             // modify its rules to exclude the uid
470             if (!mix->hasUidRule(false /*match*/, uid)) {
471                 // no need to do it again if uid is already excluded
472                 mix->setExcludeUid(uid);
473             }
474         }
475     }
476 
477     return NO_ERROR;
478 }
479 
removeUidDeviceAffinities(uid_t uid)480 status_t AudioPolicyMixCollection::removeUidDeviceAffinities(uid_t uid) {
481     // for each player mix: remove existing rules that match or exclude this uid
482     for (size_t i = 0; i < size(); i++) {
483         bool foundUidRule = false;
484         const AudioPolicyMix *mix = itemAt(i).get();
485         if (!mix->isDeviceAffinityCompatible()) {
486             continue;
487         }
488         std::vector<size_t> criteriaToRemove;
489         for (size_t j = 0; j < mix->mCriteria.size(); j++) {
490             const uint32_t rule = mix->mCriteria[j].mRule;
491             // is this rule excluding the uid? (not considering uid match rules
492             // as those are not used for uid-device affinity)
493             if (rule == RULE_EXCLUDE_UID
494                     && uid == mix->mCriteria[j].mValue.mUid) {
495                 foundUidRule = true;
496                 criteriaToRemove.insert(criteriaToRemove.begin(), j);
497             }
498         }
499         if (foundUidRule) {
500             for (size_t j = 0; j < criteriaToRemove.size(); j++) {
501                 mix->mCriteria.removeAt(criteriaToRemove[j]);
502             }
503         }
504     }
505     return NO_ERROR;
506 }
507 
getDevicesForUid(uid_t uid,Vector<AudioDeviceTypeAddr> & devices) const508 status_t AudioPolicyMixCollection::getDevicesForUid(uid_t uid,
509         Vector<AudioDeviceTypeAddr>& devices) const {
510     // for each player mix: find rules that don't exclude this uid, and add the device to the list
511     for (size_t i = 0; i < size(); i++) {
512         bool ruleAllowsUid = true;
513         const AudioPolicyMix *mix = itemAt(i).get();
514         if (mix->mMixType != MIX_TYPE_PLAYERS) {
515             continue;
516         }
517         for (size_t j = 0; j < mix->mCriteria.size(); j++) {
518             const uint32_t rule = mix->mCriteria[j].mRule;
519             if (rule == RULE_EXCLUDE_UID
520                     && uid == mix->mCriteria[j].mValue.mUid) {
521                 ruleAllowsUid = false;
522                 break;
523             }
524         }
525         if (ruleAllowsUid) {
526             devices.add(AudioDeviceTypeAddr(mix->mDeviceType, mix->mDeviceAddress));
527         }
528     }
529     return NO_ERROR;
530 }
531 
dump(String8 * dst) const532 void AudioPolicyMixCollection::dump(String8 *dst) const
533 {
534     dst->append("\nAudio Policy Mix:\n");
535     for (size_t i = 0; i < size(); i++) {
536         itemAt(i)->dump(dst, 2, i);
537     }
538 }
539 
540 }; //namespace android
541