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