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