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,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, 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, 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,uid_t uid)204 AudioPolicyMixCollection::MixMatchStatus AudioPolicyMixCollection::mixMatch(
205 const AudioMix* mix, size_t mixIndex, const audio_attributes_t& attributes, uid_t uid) {
206
207 if (mix->mMixType == MIX_TYPE_PLAYERS) {
208 // Loopback render mixes are created from a public API and thus restricted
209 // to non sensible audio that have not opted out.
210 if (is_mix_loopback_render(mix->mRouteFlags)) {
211 if (!(attributes.usage == AUDIO_USAGE_UNKNOWN ||
212 attributes.usage == AUDIO_USAGE_MEDIA ||
213 attributes.usage == AUDIO_USAGE_GAME ||
214 attributes.usage == AUDIO_USAGE_VOICE_COMMUNICATION)) {
215 return MixMatchStatus::NO_MATCH;
216 }
217 auto hasFlag = [](auto flags, auto flag) { return (flags & flag) == flag; };
218 if (hasFlag(attributes.flags, AUDIO_FLAG_NO_SYSTEM_CAPTURE)) {
219 return MixMatchStatus::NO_MATCH;
220 }
221
222 if (attributes.usage == AUDIO_USAGE_VOICE_COMMUNICATION) {
223 if (!mix->mVoiceCommunicationCaptureAllowed) {
224 return MixMatchStatus::NO_MATCH;
225 }
226 } else if (!mix->mAllowPrivilegedMediaPlaybackCapture &&
227 hasFlag(attributes.flags, AUDIO_FLAG_NO_MEDIA_PROJECTION)) {
228 return MixMatchStatus::NO_MATCH;
229 }
230 }
231
232 int userId = (int) multiuser_get_user_id(uid);
233
234 // TODO if adding more player rules (currently only 2), make rule handling "generic"
235 // as there is no difference in the treatment of usage- or uid-based rules
236 bool hasUsageMatchRules = false;
237 bool hasUsageExcludeRules = false;
238 bool usageMatchFound = false;
239 bool usageExclusionFound = false;
240
241 bool hasUidMatchRules = false;
242 bool hasUidExcludeRules = false;
243 bool uidMatchFound = false;
244 bool uidExclusionFound = false;
245
246 bool hasUserIdExcludeRules = false;
247 bool userIdExclusionFound = false;
248 bool hasUserIdMatchRules = false;
249 bool userIdMatchFound = false;
250
251
252 bool hasAddrMatch = false;
253
254 // iterate over all mix criteria to list what rules this mix contains
255 for (size_t j = 0; j < mix->mCriteria.size(); j++) {
256 ALOGV(" getOutputForAttr: mix %zu: inspecting mix criteria %zu of %zu",
257 mixIndex, j, mix->mCriteria.size());
258
259 // if there is an address match, prioritize that match
260 if (strncmp(attributes.tags, "addr=", strlen("addr=")) == 0 &&
261 strncmp(attributes.tags + strlen("addr="),
262 mix->mDeviceAddress.string(),
263 AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - strlen("addr=") - 1) == 0) {
264 hasAddrMatch = true;
265 break;
266 }
267
268 switch (mix->mCriteria[j].mRule) {
269 case RULE_MATCH_ATTRIBUTE_USAGE:
270 ALOGV("\tmix has RULE_MATCH_ATTRIBUTE_USAGE for usage %d",
271 mix->mCriteria[j].mValue.mUsage);
272 hasUsageMatchRules = true;
273 if (mix->mCriteria[j].mValue.mUsage == attributes.usage) {
274 // found one match against all allowed usages
275 usageMatchFound = true;
276 }
277 break;
278 case RULE_EXCLUDE_ATTRIBUTE_USAGE:
279 ALOGV("\tmix has RULE_EXCLUDE_ATTRIBUTE_USAGE for usage %d",
280 mix->mCriteria[j].mValue.mUsage);
281 hasUsageExcludeRules = true;
282 if (mix->mCriteria[j].mValue.mUsage == attributes.usage) {
283 // found this usage is to be excluded
284 usageExclusionFound = true;
285 }
286 break;
287 case RULE_MATCH_UID:
288 ALOGV("\tmix has RULE_MATCH_UID for uid %d", mix->mCriteria[j].mValue.mUid);
289 hasUidMatchRules = true;
290 if (mix->mCriteria[j].mValue.mUid == uid) {
291 // found one UID match against all allowed UIDs
292 uidMatchFound = true;
293 }
294 break;
295 case RULE_EXCLUDE_UID:
296 ALOGV("\tmix has RULE_EXCLUDE_UID for uid %d", mix->mCriteria[j].mValue.mUid);
297 hasUidExcludeRules = true;
298 if (mix->mCriteria[j].mValue.mUid == uid) {
299 // found this UID is to be excluded
300 uidExclusionFound = true;
301 }
302 break;
303 case RULE_MATCH_USERID:
304 ALOGV("\tmix has RULE_MATCH_USERID for userId %d",
305 mix->mCriteria[j].mValue.mUserId);
306 hasUserIdMatchRules = true;
307 if (mix->mCriteria[j].mValue.mUserId == userId) {
308 // found one userId match against all allowed userIds
309 userIdMatchFound = true;
310 }
311 break;
312 case RULE_EXCLUDE_USERID:
313 ALOGV("\tmix has RULE_EXCLUDE_USERID for userId %d",
314 mix->mCriteria[j].mValue.mUserId);
315 hasUserIdExcludeRules = true;
316 if (mix->mCriteria[j].mValue.mUserId == userId) {
317 // found this userId is to be excluded
318 userIdExclusionFound = true;
319 }
320 break;
321 default:
322 break;
323 }
324
325 // consistency checks: for each "dimension" of rules (usage, uid...), we can
326 // only have MATCH rules, or EXCLUDE rules in each dimension, not a combination
327 if (hasUsageMatchRules && hasUsageExcludeRules) {
328 ALOGE("getOutputForAttr: invalid combination of RULE_MATCH_ATTRIBUTE_USAGE"
329 " and RULE_EXCLUDE_ATTRIBUTE_USAGE in mix %zu", mixIndex);
330 return MixMatchStatus::INVALID_MIX;
331 }
332 if (hasUidMatchRules && hasUidExcludeRules) {
333 ALOGE("getOutputForAttr: invalid combination of RULE_MATCH_UID"
334 " and RULE_EXCLUDE_UID in mix %zu", mixIndex);
335 return MixMatchStatus::INVALID_MIX;
336 }
337 if (hasUserIdMatchRules && hasUserIdExcludeRules) {
338 ALOGE("getOutputForAttr: invalid combination of RULE_MATCH_USERID"
339 " and RULE_EXCLUDE_USERID in mix %zu", mixIndex);
340 return MixMatchStatus::INVALID_MIX;
341 }
342
343 if ((hasUsageExcludeRules && usageExclusionFound)
344 || (hasUidExcludeRules && uidExclusionFound)
345 || (hasUserIdExcludeRules && userIdExclusionFound)) {
346 break; // stop iterating on criteria because an exclusion was found (will fail)
347 }
348 }//iterate on mix criteria
349
350 // determine if exiting on success (or implicit failure as desc is 0)
351 if (hasAddrMatch ||
352 !((hasUsageExcludeRules && usageExclusionFound) ||
353 (hasUsageMatchRules && !usageMatchFound) ||
354 (hasUidExcludeRules && uidExclusionFound) ||
355 (hasUidMatchRules && !uidMatchFound) ||
356 (hasUserIdExcludeRules && userIdExclusionFound) ||
357 (hasUserIdMatchRules && !userIdMatchFound))) {
358 ALOGV("\tgetOutputForAttr will use mix %zu", mixIndex);
359 return MixMatchStatus::MATCH;
360 }
361
362 } else if (mix->mMixType == MIX_TYPE_RECORDERS) {
363 if (attributes.usage == AUDIO_USAGE_VIRTUAL_SOURCE &&
364 strncmp(attributes.tags, "addr=", strlen("addr=")) == 0 &&
365 strncmp(attributes.tags + strlen("addr="),
366 mix->mDeviceAddress.string(),
367 AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - strlen("addr=") - 1) == 0) {
368 return MixMatchStatus::MATCH;
369 }
370 }
371 return MixMatchStatus::NO_MATCH;
372 }
373
getDeviceAndMixForOutput(const sp<SwAudioOutputDescriptor> & output,const DeviceVector & availableOutputDevices)374 sp<DeviceDescriptor> AudioPolicyMixCollection::getDeviceAndMixForOutput(
375 const sp<SwAudioOutputDescriptor> &output,
376 const DeviceVector &availableOutputDevices)
377 {
378 for (size_t i = 0; i < size(); i++) {
379 if (itemAt(i)->getOutput() == output) {
380 // This Desc is involved in a Mix, which has the highest prio
381 audio_devices_t deviceType = itemAt(i)->mDeviceType;
382 String8 address = itemAt(i)->mDeviceAddress;
383 ALOGV("%s: device (0x%x, addr=%s) forced by mix",
384 __FUNCTION__, deviceType, address.c_str());
385 return availableOutputDevices.getDevice(deviceType, address, AUDIO_FORMAT_DEFAULT);
386 }
387 }
388 return nullptr;
389 }
390
getDeviceAndMixForInputSource(audio_source_t inputSource,const DeviceVector & availDevices,uid_t uid,sp<AudioPolicyMix> * policyMix) const391 sp<DeviceDescriptor> AudioPolicyMixCollection::getDeviceAndMixForInputSource(
392 audio_source_t inputSource,
393 const DeviceVector &availDevices,
394 uid_t uid,
395 sp<AudioPolicyMix> *policyMix) const
396 {
397 for (size_t i = 0; i < size(); i++) {
398 AudioPolicyMix *mix = itemAt(i).get();
399 if (mix->mMixType != MIX_TYPE_RECORDERS) {
400 continue;
401 }
402 for (size_t j = 0; j < mix->mCriteria.size(); j++) {
403 if ((RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET == mix->mCriteria[j].mRule &&
404 mix->mCriteria[j].mValue.mSource == inputSource) ||
405 (RULE_EXCLUDE_ATTRIBUTE_CAPTURE_PRESET == mix->mCriteria[j].mRule &&
406 mix->mCriteria[j].mValue.mSource != inputSource) ||
407 (RULE_MATCH_UID == mix->mCriteria[j].mRule &&
408 mix->mCriteria[j].mValue.mUid == uid) ||
409 (RULE_EXCLUDE_UID == mix->mCriteria[j].mRule &&
410 mix->mCriteria[j].mValue.mUid != uid)) {
411 // assuming PolicyMix only for remote submix for input
412 // so mix->mDeviceType can only be AUDIO_DEVICE_OUT_REMOTE_SUBMIX
413 audio_devices_t device = AUDIO_DEVICE_IN_REMOTE_SUBMIX;
414 auto mixDevice =
415 availDevices.getDevice(device, mix->mDeviceAddress, AUDIO_FORMAT_DEFAULT);
416 if (mixDevice != nullptr) {
417 if (policyMix != nullptr) {
418 *policyMix = mix;
419 }
420 return mixDevice;
421 }
422 break;
423 }
424 }
425 }
426 return nullptr;
427 }
428
getInputMixForAttr(audio_attributes_t attr,sp<AudioPolicyMix> * policyMix)429 status_t AudioPolicyMixCollection::getInputMixForAttr(
430 audio_attributes_t attr, sp<AudioPolicyMix> *policyMix)
431 {
432 if (strncmp(attr.tags, "addr=", strlen("addr=")) != 0) {
433 return BAD_VALUE;
434 }
435 String8 address(attr.tags + strlen("addr="));
436
437 #ifdef LOG_NDEBUG
438 ALOGV("getInputMixForAttr looking for address %s for source %d\n mixes available:",
439 address.string(), attr.source);
440 for (size_t i = 0; i < size(); i++) {
441 const sp<AudioPolicyMix> audioPolicyMix = itemAt(i);
442 ALOGV("\tmix %zu address=%s", i, audioPolicyMix->mDeviceAddress.string());
443 }
444 #endif
445
446 size_t index;
447 for (index = 0; index < size(); index++) {
448 const sp<AudioPolicyMix>& registeredMix = itemAt(index);
449 if (registeredMix->mDeviceAddress.compare(address) == 0) {
450 ALOGD("getInputMixForAttr found addr=%s dev=0x%x",
451 registeredMix->mDeviceAddress.string(), registeredMix->mDeviceType);
452 break;
453 }
454 }
455 if (index == size()) {
456 ALOGW("getInputMixForAttr() no policy for address %s", address.string());
457 return BAD_VALUE;
458 }
459 const sp<AudioPolicyMix> audioPolicyMix = itemAt(index);
460
461 if (audioPolicyMix->mMixType != MIX_TYPE_PLAYERS) {
462 ALOGW("getInputMixForAttr() bad policy mix type for address %s", address.string());
463 return BAD_VALUE;
464 }
465 if (policyMix != nullptr) {
466 *policyMix = audioPolicyMix;
467 }
468 return NO_ERROR;
469 }
470
setUidDeviceAffinities(uid_t uid,const AudioDeviceTypeAddrVector & devices)471 status_t AudioPolicyMixCollection::setUidDeviceAffinities(uid_t uid,
472 const AudioDeviceTypeAddrVector& devices) {
473 // verify feasibility: for each player mix: if it already contains a
474 // "match uid" rule for this uid, return an error
475 // (adding a uid-device affinity would result in contradictory rules)
476 for (size_t i = 0; i < size(); i++) {
477 const AudioPolicyMix* mix = itemAt(i).get();
478 if (!mix->isDeviceAffinityCompatible()) {
479 continue;
480 }
481 if (mix->hasUidRule(true /*match*/, uid)) {
482 return INVALID_OPERATION;
483 }
484 }
485
486 // remove existing rules for this uid
487 removeUidDeviceAffinities(uid);
488
489 // for each player mix:
490 // IF device is not a target for the mix,
491 // AND it doesn't have a "match uid" rule
492 // THEN add a rule to exclude the uid
493 for (size_t i = 0; i < size(); i++) {
494 const AudioPolicyMix *mix = itemAt(i).get();
495 if (!mix->isDeviceAffinityCompatible()) {
496 continue;
497 }
498 // check if this mix goes to a device in the list of devices
499 bool deviceMatch = false;
500 const AudioDeviceTypeAddr mixDevice(mix->mDeviceType, mix->mDeviceAddress.string());
501 for (size_t j = 0; j < devices.size(); j++) {
502 if (mixDevice.equals(devices[j])) {
503 deviceMatch = true;
504 break;
505 }
506 }
507 if (!deviceMatch && !mix->hasMatchUidRule()) {
508 // this mix doesn't go to one of the listed devices for the given uid,
509 // and it's not already restricting the mix on a uid,
510 // modify its rules to exclude the uid
511 if (!mix->hasUidRule(false /*match*/, uid)) {
512 // no need to do it again if uid is already excluded
513 mix->setExcludeUid(uid);
514 }
515 }
516 }
517
518 return NO_ERROR;
519 }
520
removeUidDeviceAffinities(uid_t uid)521 status_t AudioPolicyMixCollection::removeUidDeviceAffinities(uid_t uid) {
522 // for each player mix: remove existing rules that match or exclude this uid
523 for (size_t i = 0; i < size(); i++) {
524 bool foundUidRule = false;
525 const AudioPolicyMix *mix = itemAt(i).get();
526 if (!mix->isDeviceAffinityCompatible()) {
527 continue;
528 }
529 std::vector<size_t> criteriaToRemove;
530 for (size_t j = 0; j < mix->mCriteria.size(); j++) {
531 const uint32_t rule = mix->mCriteria[j].mRule;
532 // is this rule excluding the uid? (not considering uid match rules
533 // as those are not used for uid-device affinity)
534 if (rule == RULE_EXCLUDE_UID
535 && uid == mix->mCriteria[j].mValue.mUid) {
536 foundUidRule = true;
537 criteriaToRemove.insert(criteriaToRemove.begin(), j);
538 }
539 }
540 if (foundUidRule) {
541 for (size_t j = 0; j < criteriaToRemove.size(); j++) {
542 mix->mCriteria.removeAt(criteriaToRemove[j]);
543 }
544 }
545 }
546 return NO_ERROR;
547 }
548
getDevicesForUid(uid_t uid,Vector<AudioDeviceTypeAddr> & devices) const549 status_t AudioPolicyMixCollection::getDevicesForUid(uid_t uid,
550 Vector<AudioDeviceTypeAddr>& devices) const {
551 // for each player mix: find rules that don't exclude this uid, and add the device to the list
552 for (size_t i = 0; i < size(); i++) {
553 bool ruleAllowsUid = true;
554 const AudioPolicyMix *mix = itemAt(i).get();
555 if (mix->mMixType != MIX_TYPE_PLAYERS) {
556 continue;
557 }
558 for (size_t j = 0; j < mix->mCriteria.size(); j++) {
559 const uint32_t rule = mix->mCriteria[j].mRule;
560 if (rule == RULE_EXCLUDE_UID
561 && uid == mix->mCriteria[j].mValue.mUid) {
562 ruleAllowsUid = false;
563 break;
564 }
565 }
566 if (ruleAllowsUid) {
567 devices.add(AudioDeviceTypeAddr(mix->mDeviceType, mix->mDeviceAddress.string()));
568 }
569 }
570 return NO_ERROR;
571 }
572
setUserIdDeviceAffinities(int userId,const AudioDeviceTypeAddrVector & devices)573 status_t AudioPolicyMixCollection::setUserIdDeviceAffinities(int userId,
574 const AudioDeviceTypeAddrVector& devices) {
575 // verify feasibility: for each player mix: if it already contains a
576 // "match userId" rule for this userId, return an error
577 // (adding a userId-device affinity would result in contradictory rules)
578 for (size_t i = 0; i < size(); i++) {
579 const AudioPolicyMix* mix = itemAt(i).get();
580 if (!mix->isDeviceAffinityCompatible()) {
581 continue;
582 }
583 if (mix->hasUserIdRule(true /*match*/, userId)) {
584 return INVALID_OPERATION;
585 }
586 }
587
588 // remove existing rules for this userId
589 removeUserIdDeviceAffinities(userId);
590
591 // for each player mix:
592 // IF device is not a target for the mix,
593 // AND it doesn't have a "match userId" rule
594 // THEN add a rule to exclude the userId
595 for (size_t i = 0; i < size(); i++) {
596 const AudioPolicyMix *mix = itemAt(i).get();
597 if (!mix->isDeviceAffinityCompatible()) {
598 continue;
599 }
600 // check if this mix goes to a device in the list of devices
601 bool deviceMatch = false;
602 const AudioDeviceTypeAddr mixDevice(mix->mDeviceType, mix->mDeviceAddress.string());
603 for (size_t j = 0; j < devices.size(); j++) {
604 if (mixDevice.equals(devices[j])) {
605 deviceMatch = true;
606 break;
607 }
608 }
609 if (!deviceMatch && !mix->hasMatchUserIdRule()) {
610 // this mix doesn't go to one of the listed devices for the given userId,
611 // and it's not already restricting the mix on a userId,
612 // modify its rules to exclude the userId
613 if (!mix->hasUserIdRule(false /*match*/, userId)) {
614 // no need to do it again if userId is already excluded
615 mix->setExcludeUserId(userId);
616 }
617 }
618 }
619
620 return NO_ERROR;
621 }
622
removeUserIdDeviceAffinities(int userId)623 status_t AudioPolicyMixCollection::removeUserIdDeviceAffinities(int userId) {
624 // for each player mix: remove existing rules that match or exclude this userId
625 for (size_t i = 0; i < size(); i++) {
626 bool foundUserIdRule = false;
627 const AudioPolicyMix *mix = itemAt(i).get();
628 if (!mix->isDeviceAffinityCompatible()) {
629 continue;
630 }
631 std::vector<size_t> criteriaToRemove;
632 for (size_t j = 0; j < mix->mCriteria.size(); j++) {
633 const uint32_t rule = mix->mCriteria[j].mRule;
634 // is this rule excluding the userId? (not considering userId match rules
635 // as those are not used for userId-device affinity)
636 if (rule == RULE_EXCLUDE_USERID
637 && userId == mix->mCriteria[j].mValue.mUserId) {
638 foundUserIdRule = true;
639 criteriaToRemove.insert(criteriaToRemove.begin(), j);
640 }
641 }
642 if (foundUserIdRule) {
643 for (size_t j = 0; j < criteriaToRemove.size(); j++) {
644 mix->mCriteria.removeAt(criteriaToRemove[j]);
645 }
646 }
647 }
648 return NO_ERROR;
649 }
650
getDevicesForUserId(int userId,Vector<AudioDeviceTypeAddr> & devices) const651 status_t AudioPolicyMixCollection::getDevicesForUserId(int userId,
652 Vector<AudioDeviceTypeAddr>& devices) const {
653 // for each player mix:
654 // find rules that don't exclude this userId, and add the device to the list
655 for (size_t i = 0; i < size(); i++) {
656 bool ruleAllowsUserId = true;
657 const AudioPolicyMix *mix = itemAt(i).get();
658 if (mix->mMixType != MIX_TYPE_PLAYERS) {
659 continue;
660 }
661 for (size_t j = 0; j < mix->mCriteria.size(); j++) {
662 const uint32_t rule = mix->mCriteria[j].mRule;
663 if (rule == RULE_EXCLUDE_USERID
664 && userId == mix->mCriteria[j].mValue.mUserId) {
665 ruleAllowsUserId = false;
666 break;
667 }
668 }
669 if (ruleAllowsUserId) {
670 devices.add(AudioDeviceTypeAddr(mix->mDeviceType, mix->mDeviceAddress.string()));
671 }
672 }
673 return NO_ERROR;
674 }
675
dump(String8 * dst) const676 void AudioPolicyMixCollection::dump(String8 *dst) const
677 {
678 dst->append("\nAudio Policy Mix:\n");
679 for (size_t i = 0; i < size(); i++) {
680 itemAt(i)->dump(dst, 2, i);
681 }
682 }
683
684 }; //namespace android
685