• 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 "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 (%p):\n", spaces, "", index + 1, this);
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     dst->appendFormat("%*s- output: %d\n", spaces, "",
48             mOutput == nullptr ? 0 : mOutput->mIoHandle);
49 
50     int indexCriterion = 0;
51     for (const auto &criterion : mCriteria) {
52         dst->appendFormat("%*s- Criterion %d: ", spaces + 2, "", indexCriterion++);
53 
54         std::string ruleType, ruleValue;
55         bool unknownRule = !RuleTypeConverter::toString(criterion.mRule, ruleType);
56         switch (criterion.mRule & ~RULE_EXCLUSION_MASK) { // no need to match RULE_EXCLUDE_...
57         case RULE_MATCH_ATTRIBUTE_USAGE:
58             UsageTypeConverter::toString(criterion.mValue.mUsage, ruleValue);
59             break;
60         case RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET:
61             SourceTypeConverter::toString(criterion.mValue.mSource, ruleValue);
62             break;
63         case RULE_MATCH_UID:
64             ruleValue = std::to_string(criterion.mValue.mUid);
65             break;
66         case RULE_MATCH_USERID:
67             ruleValue = std::to_string(criterion.mValue.mUserId);
68             break;
69         default:
70             unknownRule = true;
71         }
72 
73         if (!unknownRule) {
74             dst->appendFormat("%s %s\n", ruleType.c_str(), ruleValue.c_str());
75         } else {
76             dst->appendFormat("Unknown rule type value 0x%x\n", criterion.mRule);
77         }
78     }
79 }
80 
registerMix(AudioMix mix,sp<SwAudioOutputDescriptor> desc)81 status_t AudioPolicyMixCollection::registerMix(AudioMix mix, sp<SwAudioOutputDescriptor> desc)
82 {
83     for (size_t i = 0; i < size(); i++) {
84         const sp<AudioPolicyMix>& registeredMix = itemAt(i);
85         if (mix.mDeviceType == registeredMix->mDeviceType
86                 && mix.mDeviceAddress.compare(registeredMix->mDeviceAddress) == 0) {
87             ALOGE("registerMix(): mix already registered for dev=0x%x addr=%s",
88                     mix.mDeviceType, mix.mDeviceAddress.string());
89             return BAD_VALUE;
90         }
91     }
92     sp<AudioPolicyMix> policyMix = new AudioPolicyMix(mix);
93     add(policyMix);
94     ALOGD("registerMix(): adding mix for dev=0x%x addr=%s",
95             policyMix->mDeviceType, policyMix->mDeviceAddress.string());
96 
97     if (desc != 0) {
98         desc->mPolicyMix = policyMix;
99         policyMix->setOutput(desc);
100     }
101     return NO_ERROR;
102 }
103 
unregisterMix(const AudioMix & mix)104 status_t AudioPolicyMixCollection::unregisterMix(const AudioMix& mix)
105 {
106     for (size_t i = 0; i < size(); i++) {
107         const sp<AudioPolicyMix>& registeredMix = itemAt(i);
108         if (mix.mDeviceType == registeredMix->mDeviceType
109                 && mix.mDeviceAddress.compare(registeredMix->mDeviceAddress) == 0) {
110             ALOGD("unregisterMix(): removing mix for dev=0x%x addr=%s",
111                     mix.mDeviceType, mix.mDeviceAddress.string());
112             removeAt(i);
113             return NO_ERROR;
114         }
115     }
116 
117     ALOGE("unregisterMix(): mix not registered for dev=0x%x addr=%s",
118             mix.mDeviceType, mix.mDeviceAddress.string());
119     return BAD_VALUE;
120 }
121 
getAudioPolicyMix(audio_devices_t deviceType,const String8 & address,sp<AudioPolicyMix> & policyMix) const122 status_t AudioPolicyMixCollection::getAudioPolicyMix(audio_devices_t deviceType,
123         const String8& address, sp<AudioPolicyMix> &policyMix) const
124 {
125 
126     ALOGV("getAudioPolicyMix() for dev=0x%x addr=%s", deviceType, address.string());
127     for (ssize_t i = 0; i < size(); i++) {
128         // Workaround: when an in audio policy is registered, it opens an output
129         // that tries to find the audio policy, thus the device must be ignored.
130         if (itemAt(i)->mDeviceAddress.compare(address) == 0) {
131             policyMix = itemAt(i);
132             ALOGV("getAudioPolicyMix: found mix %zu match (devType=0x%x addr=%s)",
133                     i, deviceType, address.string());
134             return NO_ERROR;
135         }
136     }
137 
138     ALOGE("getAudioPolicyMix(): mix not registered for dev=0x%x addr=%s",
139             deviceType, address.string());
140     return BAD_VALUE;
141 }
142 
closeOutput(sp<SwAudioOutputDescriptor> & desc)143 void AudioPolicyMixCollection::closeOutput(sp<SwAudioOutputDescriptor> &desc)
144 {
145     for (size_t i = 0; i < size(); i++) {
146         sp<AudioPolicyMix> policyMix = itemAt(i);
147         if (policyMix->getOutput() == desc) {
148             policyMix->clearOutput();
149         }
150     }
151 }
152 
getOutputForAttr(const audio_attributes_t & attributes,const audio_config_base_t & config,uid_t uid,audio_output_flags_t flags,sp<AudioPolicyMix> & primaryMix,std::vector<sp<AudioPolicyMix>> * secondaryMixes)153 status_t AudioPolicyMixCollection::getOutputForAttr(
154         const audio_attributes_t& attributes, const audio_config_base_t& config, uid_t uid,
155         audio_output_flags_t flags,
156         sp<AudioPolicyMix> &primaryMix,
157         std::vector<sp<AudioPolicyMix>> *secondaryMixes)
158 {
159     ALOGV("getOutputForAttr() querying %zu mixes:", size());
160     primaryMix.clear();
161     for (size_t i = 0; i < size(); i++) {
162         sp<AudioPolicyMix> policyMix = itemAt(i);
163         const bool primaryOutputMix = !is_mix_loopback_render(policyMix->mRouteFlags);
164         if (!primaryOutputMix && (flags & AUDIO_OUTPUT_FLAG_MMAP_NOIRQ)) {
165             // AAudio does not support MMAP_NO_IRQ loopback render, and there is no way with
166             // the current MmapStreamInterface::start to reject a specific client added to a shared
167             // mmap stream.
168             // As a result all MMAP_NOIRQ requests have to be rejected when an loopback render
169             // policy is present. That ensures no shared mmap stream is used when an loopback
170             // render policy is registered.
171             ALOGD("%s: Rejecting MMAP_NOIRQ request due to LOOPBACK|RENDER mix present.", __func__);
172             return INVALID_OPERATION;
173         }
174 
175         if (primaryOutputMix && primaryMix != nullptr) {
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, config, 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         if (primaryOutputMix) {
192             primaryMix = policyMix;
193             ALOGV("%s: Mix %zu: set primary desc", __func__, i);
194         } else {
195             ALOGV("%s: Add a secondary desc %zu", __func__, i);
196             if (secondaryMixes != nullptr) {
197                 secondaryMixes->push_back(policyMix);
198             }
199         }
200     }
201     return NO_ERROR;
202 }
203 
mixMatch(const AudioMix * mix,size_t mixIndex,const audio_attributes_t & attributes,const audio_config_base_t & config,uid_t uid)204 AudioPolicyMixCollection::MixMatchStatus AudioPolicyMixCollection::mixMatch(
205         const AudioMix* mix, size_t mixIndex, const audio_attributes_t& attributes,
206         const audio_config_base_t& config, uid_t uid) {
207 
208     if (mix->mMixType == MIX_TYPE_PLAYERS) {
209         // Loopback render mixes are created from a public API and thus restricted
210         // to non sensible audio that have not opted out.
211         if (is_mix_loopback_render(mix->mRouteFlags)) {
212             if (!(attributes.usage == AUDIO_USAGE_UNKNOWN ||
213                   attributes.usage == AUDIO_USAGE_MEDIA ||
214                   attributes.usage == AUDIO_USAGE_GAME ||
215                   attributes.usage == AUDIO_USAGE_VOICE_COMMUNICATION)) {
216                 return MixMatchStatus::NO_MATCH;
217             }
218             auto hasFlag = [](auto flags, auto flag) { return (flags & flag) == flag; };
219             if (hasFlag(attributes.flags, AUDIO_FLAG_NO_SYSTEM_CAPTURE)) {
220                 return MixMatchStatus::NO_MATCH;
221             }
222 
223             if (attributes.usage == AUDIO_USAGE_VOICE_COMMUNICATION) {
224                 if (!mix->mVoiceCommunicationCaptureAllowed) {
225                     return MixMatchStatus::NO_MATCH;
226                 }
227             } else if (!mix->mAllowPrivilegedMediaPlaybackCapture &&
228                 hasFlag(attributes.flags, AUDIO_FLAG_NO_MEDIA_PROJECTION)) {
229                 return MixMatchStatus::NO_MATCH;
230             }
231         }
232 
233         // Permit match only if requested format and mix format are PCM and can be format
234         // adapted by the mixer, or are the same (compressed) format.
235         if (!is_mix_loopback(mix->mRouteFlags) &&
236             !((audio_is_linear_pcm(config.format) && audio_is_linear_pcm(mix->mFormat.format)) ||
237               (config.format == mix->mFormat.format)) &&
238               config.format != AUDIO_CONFIG_BASE_INITIALIZER.format) {
239             return MixMatchStatus::NO_MATCH;
240         }
241 
242         int userId = (int) multiuser_get_user_id(uid);
243 
244         // TODO if adding more player rules (currently only 2), make rule handling "generic"
245         //      as there is no difference in the treatment of usage- or uid-based rules
246         bool hasUsageMatchRules = false;
247         bool hasUsageExcludeRules = false;
248         bool usageMatchFound = false;
249         bool usageExclusionFound = false;
250 
251         bool hasUidMatchRules = false;
252         bool hasUidExcludeRules = false;
253         bool uidMatchFound = false;
254         bool uidExclusionFound = false;
255 
256         bool hasUserIdExcludeRules = false;
257         bool userIdExclusionFound = false;
258         bool hasUserIdMatchRules = false;
259         bool userIdMatchFound = false;
260 
261 
262         bool hasAddrMatch = false;
263 
264         // iterate over all mix criteria to list what rules this mix contains
265         for (size_t j = 0; j < mix->mCriteria.size(); j++) {
266             ALOGV(" getOutputForAttr: mix %zu: inspecting mix criteria %zu of %zu",
267                     mixIndex, j, mix->mCriteria.size());
268 
269             // if there is an address match, prioritize that match
270             if (strncmp(attributes.tags, "addr=", strlen("addr=")) == 0 &&
271                     strncmp(attributes.tags + strlen("addr="),
272                             mix->mDeviceAddress.string(),
273                             AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - strlen("addr=") - 1) == 0) {
274                 hasAddrMatch = true;
275                 break;
276             }
277 
278             switch (mix->mCriteria[j].mRule) {
279             case RULE_MATCH_ATTRIBUTE_USAGE:
280                 ALOGV("\tmix has RULE_MATCH_ATTRIBUTE_USAGE for usage %d",
281                                             mix->mCriteria[j].mValue.mUsage);
282                 hasUsageMatchRules = true;
283                 if (mix->mCriteria[j].mValue.mUsage == attributes.usage) {
284                     // found one match against all allowed usages
285                     usageMatchFound = true;
286                 }
287                 break;
288             case RULE_EXCLUDE_ATTRIBUTE_USAGE:
289                 ALOGV("\tmix has RULE_EXCLUDE_ATTRIBUTE_USAGE for usage %d",
290                         mix->mCriteria[j].mValue.mUsage);
291                 hasUsageExcludeRules = true;
292                 if (mix->mCriteria[j].mValue.mUsage == attributes.usage) {
293                     // found this usage is to be excluded
294                     usageExclusionFound = true;
295                 }
296                 break;
297             case RULE_MATCH_UID:
298                 ALOGV("\tmix has RULE_MATCH_UID for uid %d", mix->mCriteria[j].mValue.mUid);
299                 hasUidMatchRules = true;
300                 if (mix->mCriteria[j].mValue.mUid == uid) {
301                     // found one UID match against all allowed UIDs
302                     uidMatchFound = true;
303                 }
304                 break;
305             case RULE_EXCLUDE_UID:
306                 ALOGV("\tmix has RULE_EXCLUDE_UID for uid %d", mix->mCriteria[j].mValue.mUid);
307                 hasUidExcludeRules = true;
308                 if (mix->mCriteria[j].mValue.mUid == uid) {
309                     // found this UID is to be excluded
310                     uidExclusionFound = true;
311                 }
312                 break;
313             case RULE_MATCH_USERID:
314                 ALOGV("\tmix has RULE_MATCH_USERID for userId %d",
315                     mix->mCriteria[j].mValue.mUserId);
316                 hasUserIdMatchRules = true;
317                 if (mix->mCriteria[j].mValue.mUserId == userId) {
318                     // found one userId match against all allowed userIds
319                     userIdMatchFound = true;
320                 }
321                 break;
322             case RULE_EXCLUDE_USERID:
323                 ALOGV("\tmix has RULE_EXCLUDE_USERID for userId %d",
324                     mix->mCriteria[j].mValue.mUserId);
325                 hasUserIdExcludeRules = true;
326                 if (mix->mCriteria[j].mValue.mUserId == userId) {
327                     // found this userId is to be excluded
328                     userIdExclusionFound = true;
329                 }
330                 break;
331             default:
332                 break;
333             }
334 
335             // consistency checks: for each "dimension" of rules (usage, uid...), we can
336             // only have MATCH rules, or EXCLUDE rules in each dimension, not a combination
337             if (hasUsageMatchRules && hasUsageExcludeRules) {
338                 ALOGE("getOutputForAttr: invalid combination of RULE_MATCH_ATTRIBUTE_USAGE"
339                         " and RULE_EXCLUDE_ATTRIBUTE_USAGE in mix %zu", mixIndex);
340                 return MixMatchStatus::INVALID_MIX;
341             }
342             if (hasUidMatchRules && hasUidExcludeRules) {
343                 ALOGE("getOutputForAttr: invalid combination of RULE_MATCH_UID"
344                         " and RULE_EXCLUDE_UID in mix %zu", mixIndex);
345                 return MixMatchStatus::INVALID_MIX;
346             }
347             if (hasUserIdMatchRules && hasUserIdExcludeRules) {
348                 ALOGE("getOutputForAttr: invalid combination of RULE_MATCH_USERID"
349                         " and RULE_EXCLUDE_USERID in mix %zu", mixIndex);
350                     return MixMatchStatus::INVALID_MIX;
351             }
352 
353             if ((hasUsageExcludeRules && usageExclusionFound)
354                     || (hasUidExcludeRules && uidExclusionFound)
355                     || (hasUserIdExcludeRules && userIdExclusionFound)) {
356                 break; // stop iterating on criteria because an exclusion was found (will fail)
357             }
358         }//iterate on mix criteria
359 
360         // determine if exiting on success (or implicit failure as desc is 0)
361         if (hasAddrMatch ||
362                 !((hasUsageExcludeRules && usageExclusionFound) ||
363                   (hasUsageMatchRules && !usageMatchFound)  ||
364                   (hasUidExcludeRules && uidExclusionFound) ||
365                   (hasUidMatchRules && !uidMatchFound) ||
366                   (hasUserIdExcludeRules && userIdExclusionFound) ||
367                   (hasUserIdMatchRules && !userIdMatchFound))) {
368             ALOGV("\tgetOutputForAttr will use mix %zu", mixIndex);
369             return MixMatchStatus::MATCH;
370         }
371 
372     } else if (mix->mMixType == MIX_TYPE_RECORDERS) {
373         if (attributes.usage == AUDIO_USAGE_VIRTUAL_SOURCE &&
374                 strncmp(attributes.tags, "addr=", strlen("addr=")) == 0 &&
375                 strncmp(attributes.tags + strlen("addr="),
376                         mix->mDeviceAddress.string(),
377                         AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - strlen("addr=") - 1) == 0) {
378             return MixMatchStatus::MATCH;
379         }
380     }
381     return MixMatchStatus::NO_MATCH;
382 }
383 
getDeviceAndMixForOutput(const sp<SwAudioOutputDescriptor> & output,const DeviceVector & availableOutputDevices)384 sp<DeviceDescriptor> AudioPolicyMixCollection::getDeviceAndMixForOutput(
385         const sp<SwAudioOutputDescriptor> &output,
386         const DeviceVector &availableOutputDevices)
387 {
388     for (size_t i = 0; i < size(); i++) {
389         if (itemAt(i)->getOutput() == output) {
390             // This Desc is involved in a Mix, which has the highest prio
391             audio_devices_t deviceType = itemAt(i)->mDeviceType;
392             String8 address = itemAt(i)->mDeviceAddress;
393             ALOGV("%s: device (0x%x, addr=%s) forced by mix",
394                   __FUNCTION__, deviceType, address.c_str());
395             return availableOutputDevices.getDevice(deviceType, address, AUDIO_FORMAT_DEFAULT);
396         }
397     }
398     return nullptr;
399 }
400 
getDeviceAndMixForInputSource(audio_source_t inputSource,const DeviceVector & availDevices,uid_t uid,sp<AudioPolicyMix> * policyMix) const401 sp<DeviceDescriptor> AudioPolicyMixCollection::getDeviceAndMixForInputSource(
402         audio_source_t inputSource,
403         const DeviceVector &availDevices,
404         uid_t uid,
405         sp<AudioPolicyMix> *policyMix) const
406 {
407     for (size_t i = 0; i < size(); i++) {
408         AudioPolicyMix *mix = itemAt(i).get();
409         if (mix->mMixType != MIX_TYPE_RECORDERS) {
410             continue;
411         }
412         for (size_t j = 0; j < mix->mCriteria.size(); j++) {
413             if ((RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET == mix->mCriteria[j].mRule &&
414                     mix->mCriteria[j].mValue.mSource == inputSource) ||
415                (RULE_EXCLUDE_ATTRIBUTE_CAPTURE_PRESET == mix->mCriteria[j].mRule &&
416                     mix->mCriteria[j].mValue.mSource != inputSource) ||
417                (RULE_MATCH_UID == mix->mCriteria[j].mRule &&
418                     mix->mCriteria[j].mValue.mUid == uid) ||
419                (RULE_EXCLUDE_UID == mix->mCriteria[j].mRule &&
420                     mix->mCriteria[j].mValue.mUid != uid)) {
421                 // assuming PolicyMix only for remote submix for input
422                 // so mix->mDeviceType can only be AUDIO_DEVICE_OUT_REMOTE_SUBMIX
423                 audio_devices_t device = AUDIO_DEVICE_IN_REMOTE_SUBMIX;
424                 auto mixDevice =
425                         availDevices.getDevice(device, mix->mDeviceAddress, AUDIO_FORMAT_DEFAULT);
426                 if (mixDevice != nullptr) {
427                     if (policyMix != nullptr) {
428                         *policyMix = mix;
429                     }
430                     return mixDevice;
431                 }
432                 break;
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     if (strncmp(attr.tags, "addr=", strlen("addr=")) != 0) {
443         return BAD_VALUE;
444     }
445     String8 address(attr.tags + strlen("addr="));
446 
447 #ifdef LOG_NDEBUG
448     ALOGV("getInputMixForAttr looking for address %s for source %d\n  mixes available:",
449             address.string(), 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 (registeredMix->mDeviceAddress.compare(address) == 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.string());
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.string());
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         const 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         bool foundUidRule = false;
535         const AudioPolicyMix *mix = itemAt(i).get();
536         if (!mix->isDeviceAffinityCompatible()) {
537             continue;
538         }
539         std::vector<size_t> criteriaToRemove;
540         for (size_t j = 0; j < mix->mCriteria.size(); j++) {
541             const uint32_t rule = mix->mCriteria[j].mRule;
542             // is this rule excluding the uid? (not considering uid match rules
543             // as those are not used for uid-device affinity)
544             if (rule == RULE_EXCLUDE_UID
545                     && uid == mix->mCriteria[j].mValue.mUid) {
546                 foundUidRule = true;
547                 criteriaToRemove.insert(criteriaToRemove.begin(), j);
548             }
549         }
550         if (foundUidRule) {
551             for (size_t j = 0; j < criteriaToRemove.size(); j++) {
552                 mix->mCriteria.removeAt(criteriaToRemove[j]);
553             }
554         }
555     }
556     return NO_ERROR;
557 }
558 
getDevicesForUid(uid_t uid,Vector<AudioDeviceTypeAddr> & devices) const559 status_t AudioPolicyMixCollection::getDevicesForUid(uid_t uid,
560         Vector<AudioDeviceTypeAddr>& devices) const {
561     // for each player mix: find rules that don't exclude this uid, and add the device to the list
562     for (size_t i = 0; i < size(); i++) {
563         bool ruleAllowsUid = true;
564         const AudioPolicyMix *mix = itemAt(i).get();
565         if (mix->mMixType != MIX_TYPE_PLAYERS) {
566             continue;
567         }
568         for (size_t j = 0; j < mix->mCriteria.size(); j++) {
569             const uint32_t rule = mix->mCriteria[j].mRule;
570             if (rule == RULE_EXCLUDE_UID
571                     && uid == mix->mCriteria[j].mValue.mUid) {
572                 ruleAllowsUid = false;
573                 break;
574             }
575         }
576         if (ruleAllowsUid) {
577             devices.add(AudioDeviceTypeAddr(mix->mDeviceType, mix->mDeviceAddress.string()));
578         }
579     }
580     return NO_ERROR;
581 }
582 
setUserIdDeviceAffinities(int userId,const AudioDeviceTypeAddrVector & devices)583 status_t AudioPolicyMixCollection::setUserIdDeviceAffinities(int userId,
584         const AudioDeviceTypeAddrVector& devices) {
585     // verify feasibility: for each player mix: if it already contains a
586     //    "match userId" rule for this userId, return an error
587     //    (adding a userId-device affinity would result in contradictory rules)
588     for (size_t i = 0; i < size(); i++) {
589         const AudioPolicyMix* mix = itemAt(i).get();
590         if (!mix->isDeviceAffinityCompatible()) {
591             continue;
592         }
593         if (mix->hasUserIdRule(true /*match*/, userId)) {
594             return INVALID_OPERATION;
595         }
596     }
597 
598     // remove existing rules for this userId
599     removeUserIdDeviceAffinities(userId);
600 
601     // for each player mix:
602     //   IF    device is not a target for the mix,
603     //     AND it doesn't have a "match userId" rule
604     //   THEN add a rule to exclude the userId
605     for (size_t i = 0; i < size(); i++) {
606         const AudioPolicyMix *mix = itemAt(i).get();
607         if (!mix->isDeviceAffinityCompatible()) {
608             continue;
609         }
610         // check if this mix goes to a device in the list of devices
611         bool deviceMatch = false;
612         const AudioDeviceTypeAddr mixDevice(mix->mDeviceType, mix->mDeviceAddress.string());
613         for (size_t j = 0; j < devices.size(); j++) {
614             if (mixDevice.equals(devices[j])) {
615                 deviceMatch = true;
616                 break;
617             }
618         }
619         if (!deviceMatch && !mix->hasMatchUserIdRule()) {
620             // this mix doesn't go to one of the listed devices for the given userId,
621             // and it's not already restricting the mix on a userId,
622             // modify its rules to exclude the userId
623             if (!mix->hasUserIdRule(false /*match*/, userId)) {
624                 // no need to do it again if userId is already excluded
625                 mix->setExcludeUserId(userId);
626             }
627         }
628     }
629 
630     return NO_ERROR;
631 }
632 
removeUserIdDeviceAffinities(int userId)633 status_t AudioPolicyMixCollection::removeUserIdDeviceAffinities(int userId) {
634     // for each player mix: remove existing rules that match or exclude this userId
635     for (size_t i = 0; i < size(); i++) {
636         bool foundUserIdRule = false;
637         const AudioPolicyMix *mix = itemAt(i).get();
638         if (!mix->isDeviceAffinityCompatible()) {
639             continue;
640         }
641         std::vector<size_t> criteriaToRemove;
642         for (size_t j = 0; j < mix->mCriteria.size(); j++) {
643             const uint32_t rule = mix->mCriteria[j].mRule;
644             // is this rule excluding the userId? (not considering userId match rules
645             // as those are not used for userId-device affinity)
646             if (rule == RULE_EXCLUDE_USERID
647                     && userId == mix->mCriteria[j].mValue.mUserId) {
648                 foundUserIdRule = true;
649                 criteriaToRemove.insert(criteriaToRemove.begin(), j);
650             }
651         }
652         if (foundUserIdRule) {
653             for (size_t j = 0; j < criteriaToRemove.size(); j++) {
654                 mix->mCriteria.removeAt(criteriaToRemove[j]);
655             }
656         }
657     }
658     return NO_ERROR;
659 }
660 
getDevicesForUserId(int userId,Vector<AudioDeviceTypeAddr> & devices) const661 status_t AudioPolicyMixCollection::getDevicesForUserId(int userId,
662         Vector<AudioDeviceTypeAddr>& devices) const {
663     // for each player mix:
664     // find rules that don't exclude this userId, and add the device to the list
665     for (size_t i = 0; i < size(); i++) {
666         bool ruleAllowsUserId = true;
667         const AudioPolicyMix *mix = itemAt(i).get();
668         if (mix->mMixType != MIX_TYPE_PLAYERS) {
669             continue;
670         }
671         for (size_t j = 0; j < mix->mCriteria.size(); j++) {
672             const uint32_t rule = mix->mCriteria[j].mRule;
673             if (rule == RULE_EXCLUDE_USERID
674                     && userId == mix->mCriteria[j].mValue.mUserId) {
675                 ruleAllowsUserId = false;
676                 break;
677             }
678         }
679         if (ruleAllowsUserId) {
680             devices.add(AudioDeviceTypeAddr(mix->mDeviceType, mix->mDeviceAddress.string()));
681         }
682     }
683     return NO_ERROR;
684 }
685 
dump(String8 * dst) const686 void AudioPolicyMixCollection::dump(String8 *dst) const
687 {
688     dst->append("\n Audio Policy Mix:\n");
689     for (size_t i = 0; i < size(); i++) {
690         itemAt(i)->dump(dst, 2, i);
691     }
692 }
693 
694 }; //namespace android
695