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 "HwModule.h"
22 #include "AudioPort.h"
23 #include "IOProfile.h"
24 #include "AudioGain.h"
25 #include <AudioOutputDescriptor.h>
26
27 namespace android {
28
setOutput(sp<SwAudioOutputDescriptor> & output)29 void AudioPolicyMix::setOutput(sp<SwAudioOutputDescriptor> &output)
30 {
31 mOutput = output;
32 }
33
getOutput() const34 const sp<SwAudioOutputDescriptor> &AudioPolicyMix::getOutput() const
35 {
36 return mOutput;
37 }
38
clearOutput()39 void AudioPolicyMix::clearOutput()
40 {
41 mOutput.clear();
42 }
43
setMix(AudioMix & mix)44 void AudioPolicyMix::setMix(AudioMix &mix)
45 {
46 mMix = mix;
47 }
48
getMix()49 android::AudioMix *AudioPolicyMix::getMix()
50 {
51 return &mMix;
52 }
53
registerMix(String8 address,AudioMix mix,sp<SwAudioOutputDescriptor> desc)54 status_t AudioPolicyMixCollection::registerMix(String8 address, AudioMix mix,
55 sp<SwAudioOutputDescriptor> desc)
56 {
57 ssize_t index = indexOfKey(address);
58 if (index >= 0) {
59 ALOGE("registerPolicyMixes(): mix for address %s already registered", address.string());
60 return BAD_VALUE;
61 }
62 sp<AudioPolicyMix> policyMix = new AudioPolicyMix();
63 policyMix->setMix(mix);
64 add(address, policyMix);
65
66 if (desc != 0) {
67 desc->mPolicyMix = policyMix->getMix();
68 policyMix->setOutput(desc);
69 }
70 return NO_ERROR;
71 }
72
unregisterMix(String8 address)73 status_t AudioPolicyMixCollection::unregisterMix(String8 address)
74 {
75 ssize_t index = indexOfKey(address);
76 if (index < 0) {
77 ALOGE("unregisterPolicyMixes(): mix for address %s not registered", address.string());
78 return BAD_VALUE;
79 }
80
81 removeItemsAt(index);
82 return NO_ERROR;
83 }
84
getAudioPolicyMix(String8 address,sp<AudioPolicyMix> & policyMix) const85 status_t AudioPolicyMixCollection::getAudioPolicyMix(String8 address,
86 sp<AudioPolicyMix> &policyMix) const
87 {
88 ssize_t index = indexOfKey(address);
89 if (index < 0) {
90 ALOGE("unregisterPolicyMixes(): mix for address %s not registered", address.string());
91 return BAD_VALUE;
92 }
93 policyMix = valueAt(index);
94 return NO_ERROR;
95 }
96
closeOutput(sp<SwAudioOutputDescriptor> & desc)97 void AudioPolicyMixCollection::closeOutput(sp<SwAudioOutputDescriptor> &desc)
98 {
99 for (size_t i = 0; i < size(); i++) {
100 sp<AudioPolicyMix> policyMix = valueAt(i);
101 if (policyMix->getOutput() == desc) {
102 policyMix->clearOutput();
103 }
104 }
105 }
106
getOutputForAttr(audio_attributes_t attributes,uid_t uid,sp<SwAudioOutputDescriptor> & desc)107 status_t AudioPolicyMixCollection::getOutputForAttr(audio_attributes_t attributes, uid_t uid,
108 sp<SwAudioOutputDescriptor> &desc)
109 {
110 ALOGV("getOutputForAttr() querying %zu mixes:", size());
111 desc = 0;
112 for (size_t i = 0; i < size(); i++) {
113 sp<AudioPolicyMix> policyMix = valueAt(i);
114 AudioMix *mix = policyMix->getMix();
115
116 if (mix->mMixType == MIX_TYPE_PLAYERS) {
117 // TODO if adding more player rules (currently only 2), make rule handling "generic"
118 // as there is no difference in the treatment of usage- or uid-based rules
119 bool hasUsageMatchRules = false;
120 bool hasUsageExcludeRules = false;
121 bool usageMatchFound = false;
122 bool usageExclusionFound = false;
123
124 bool hasUidMatchRules = false;
125 bool hasUidExcludeRules = false;
126 bool uidMatchFound = false;
127 bool uidExclusionFound = false;
128
129 bool hasAddrMatch = false;
130
131 // iterate over all mix criteria to list what rules this mix contains
132 for (size_t j = 0; j < mix->mCriteria.size(); j++) {
133 ALOGV(" getOutputForAttr: mix %zu: inspecting mix criteria %zu of %zu",
134 i, j, mix->mCriteria.size());
135
136 // if there is an address match, prioritize that match
137 if (strncmp(attributes.tags, "addr=", strlen("addr=")) == 0 &&
138 strncmp(attributes.tags + strlen("addr="),
139 mix->mDeviceAddress.string(),
140 AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - strlen("addr=") - 1) == 0) {
141 hasAddrMatch = true;
142 break;
143 }
144
145 switch (mix->mCriteria[j].mRule) {
146 case RULE_MATCH_ATTRIBUTE_USAGE:
147 ALOGV("\tmix has RULE_MATCH_ATTRIBUTE_USAGE for usage %d",
148 mix->mCriteria[j].mValue.mUsage);
149 hasUsageMatchRules = true;
150 if (mix->mCriteria[j].mValue.mUsage == attributes.usage) {
151 // found one match against all allowed usages
152 usageMatchFound = true;
153 }
154 break;
155 case RULE_EXCLUDE_ATTRIBUTE_USAGE:
156 ALOGV("\tmix has RULE_EXCLUDE_ATTRIBUTE_USAGE for usage %d",
157 mix->mCriteria[j].mValue.mUsage);
158 hasUsageExcludeRules = true;
159 if (mix->mCriteria[j].mValue.mUsage == attributes.usage) {
160 // found this usage is to be excluded
161 usageExclusionFound = true;
162 }
163 break;
164 case RULE_MATCH_UID:
165 ALOGV("\tmix has RULE_MATCH_UID for uid %d", mix->mCriteria[j].mValue.mUid);
166 hasUidMatchRules = true;
167 if (mix->mCriteria[j].mValue.mUid == uid) {
168 // found one UID match against all allowed UIDs
169 uidMatchFound = true;
170 }
171 break;
172 case RULE_EXCLUDE_UID:
173 ALOGV("\tmix has RULE_EXCLUDE_UID for uid %d", mix->mCriteria[j].mValue.mUid);
174 hasUidExcludeRules = true;
175 if (mix->mCriteria[j].mValue.mUid == uid) {
176 // found this UID is to be excluded
177 uidExclusionFound = true;
178 }
179 break;
180 default:
181 break;
182 }
183
184 // consistency checks: for each "dimension" of rules (usage, uid...), we can
185 // only have MATCH rules, or EXCLUDE rules in each dimension, not a combination
186 if (hasUsageMatchRules && hasUsageExcludeRules) {
187 ALOGE("getOutputForAttr: invalid combination of RULE_MATCH_ATTRIBUTE_USAGE"
188 " and RULE_EXCLUDE_ATTRIBUTE_USAGE in mix %zu", i);
189 return BAD_VALUE;
190 }
191 if (hasUidMatchRules && hasUidExcludeRules) {
192 ALOGE("getOutputForAttr: invalid combination of RULE_MATCH_UID"
193 " and RULE_EXCLUDE_UID in mix %zu", i);
194 return BAD_VALUE;
195 }
196
197 if ((hasUsageExcludeRules && usageExclusionFound)
198 || (hasUidExcludeRules && uidExclusionFound)) {
199 break; // stop iterating on criteria because an exclusion was found (will fail)
200 }
201
202 }//iterate on mix criteria
203
204 // determine if exiting on success (or implicit failure as desc is 0)
205 if (hasAddrMatch ||
206 !((hasUsageExcludeRules && usageExclusionFound) ||
207 (hasUsageMatchRules && !usageMatchFound) ||
208 (hasUidExcludeRules && uidExclusionFound) ||
209 (hasUidMatchRules && !uidMatchFound))) {
210 ALOGV("\tgetOutputForAttr will use mix %zu", i);
211 desc = policyMix->getOutput();
212 }
213
214 } else if (mix->mMixType == MIX_TYPE_RECORDERS) {
215 if (attributes.usage == AUDIO_USAGE_VIRTUAL_SOURCE &&
216 strncmp(attributes.tags, "addr=", strlen("addr=")) == 0 &&
217 strncmp(attributes.tags + strlen("addr="),
218 mix->mDeviceAddress.string(),
219 AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - strlen("addr=") - 1) == 0) {
220 desc = policyMix->getOutput();
221 }
222 }
223 if (desc != 0) {
224 desc->mPolicyMix = mix;
225 return NO_ERROR;
226 }
227 }
228 return BAD_VALUE;
229 }
230
getDeviceAndMixForInputSource(audio_source_t inputSource,audio_devices_t availDevices,AudioMix ** policyMix)231 audio_devices_t AudioPolicyMixCollection::getDeviceAndMixForInputSource(audio_source_t inputSource,
232 audio_devices_t availDevices,
233 AudioMix **policyMix)
234 {
235 for (size_t i = 0; i < size(); i++) {
236 AudioMix *mix = valueAt(i)->getMix();
237
238 if (mix->mMixType != MIX_TYPE_RECORDERS) {
239 continue;
240 }
241 for (size_t j = 0; j < mix->mCriteria.size(); j++) {
242 if ((RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET == mix->mCriteria[j].mRule &&
243 mix->mCriteria[j].mValue.mSource == inputSource) ||
244 (RULE_EXCLUDE_ATTRIBUTE_CAPTURE_PRESET == mix->mCriteria[j].mRule &&
245 mix->mCriteria[j].mValue.mSource != inputSource)) {
246 if (availDevices & AUDIO_DEVICE_IN_REMOTE_SUBMIX) {
247 if (policyMix != NULL) {
248 *policyMix = mix;
249 }
250 return AUDIO_DEVICE_IN_REMOTE_SUBMIX;
251 }
252 break;
253 }
254 }
255 }
256 return AUDIO_DEVICE_NONE;
257 }
258
getInputMixForAttr(audio_attributes_t attr,AudioMix ** policyMix)259 status_t AudioPolicyMixCollection::getInputMixForAttr(audio_attributes_t attr, AudioMix **policyMix)
260 {
261 if (strncmp(attr.tags, "addr=", strlen("addr=")) != 0) {
262 return BAD_VALUE;
263 }
264 String8 address(attr.tags + strlen("addr="));
265
266 #ifdef LOG_NDEBUG
267 ALOGV("getInputMixForAttr looking for address %s\n mixes available:", address.string());
268 for (size_t i = 0; i < size(); i++) {
269 sp<AudioPolicyMix> policyMix = valueAt(i);
270 AudioMix *mix = policyMix->getMix();
271 ALOGV("\tmix %zu address=%s", i, mix->mDeviceAddress.string());
272 }
273 #endif
274
275 ssize_t index = indexOfKey(address);
276 if (index < 0) {
277 ALOGW("getInputMixForAttr() no policy for address %s", address.string());
278 return BAD_VALUE;
279 }
280 sp<AudioPolicyMix> audioPolicyMix = valueAt(index);
281 AudioMix *mix = audioPolicyMix->getMix();
282
283 if (mix->mMixType != MIX_TYPE_PLAYERS) {
284 ALOGW("getInputMixForAttr() bad policy mix type for address %s", address.string());
285 return BAD_VALUE;
286 }
287 *policyMix = mix;
288 return NO_ERROR;
289 }
290
291 }; //namespace android
292