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