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 "AudioPort.h"
24 #include "IOProfile.h"
25 #include "AudioGain.h"
26 #include <AudioOutputDescriptor.h>
27
28 namespace android {
29
AudioPolicyMix(const AudioMix & mix)30 AudioPolicyMix::AudioPolicyMix(const AudioMix &mix) : AudioMix(mix)
31 {
32 }
33
setOutput(sp<SwAudioOutputDescriptor> & output)34 void AudioPolicyMix::setOutput(sp<SwAudioOutputDescriptor> &output)
35 {
36 mOutput = output;
37 }
38
getOutput() const39 const sp<SwAudioOutputDescriptor> &AudioPolicyMix::getOutput() const
40 {
41 return mOutput;
42 }
43
clearOutput()44 void AudioPolicyMix::clearOutput()
45 {
46 mOutput.clear();
47 }
48
dump(int fd,int spaces,int index) const49 status_t AudioPolicyMix::dump(int fd, int spaces, int index) const
50 {
51 const size_t SIZE = 256;
52 char buffer[SIZE];
53 String8 result;
54
55 snprintf(buffer, SIZE, "%*sAudio Policy Mix %d:\n", spaces, "", index+1);
56 result.append(buffer);
57 std::string mixTypeLiteral;
58 if (!MixTypeConverter::toString(mMixType, mixTypeLiteral)) {
59 ALOGE("%s: failed to convert mix type %d", __FUNCTION__, mMixType);
60 return BAD_VALUE;
61 }
62 snprintf(buffer, SIZE, "%*s- mix type: %s\n", spaces, "", mixTypeLiteral.c_str());
63 result.append(buffer);
64 std::string routeFlagLiteral;
65 RouteFlagTypeConverter::maskToString(mRouteFlags, routeFlagLiteral);
66 snprintf(buffer, SIZE, "%*s- Route Flags: %s\n", spaces, "", routeFlagLiteral.c_str());
67 result.append(buffer);
68 std::string deviceLiteral;
69 deviceToString(mDeviceType, deviceLiteral);
70 snprintf(buffer, SIZE, "%*s- device type: %s\n", spaces, "", deviceLiteral.c_str());
71 result.append(buffer);
72 snprintf(buffer, SIZE, "%*s- device address: %s\n", spaces, "", mDeviceAddress.string());
73 result.append(buffer);
74
75 int indexCriterion = 0;
76 for (const auto &criterion : mCriteria) {
77 snprintf(buffer, SIZE, "%*s- Criterion %d:\n", spaces + 2, "", indexCriterion++);
78 result.append(buffer);
79 std::string usageLiteral;
80 if (!UsageTypeConverter::toString(criterion.mValue.mUsage, usageLiteral)) {
81 ALOGE("%s: failed to convert usage %d", __FUNCTION__, criterion.mValue.mUsage);
82 return BAD_VALUE;
83 }
84 snprintf(buffer, SIZE, "%*s- Usage:%s\n", spaces + 4, "", usageLiteral.c_str());
85 result.append(buffer);
86 if (mMixType == MIX_TYPE_RECORDERS) {
87 std::string sourceLiteral;
88 if (!SourceTypeConverter::toString(criterion.mValue.mSource, sourceLiteral)) {
89 ALOGE("%s: failed to convert source %d", __FUNCTION__, criterion.mValue.mSource);
90 return BAD_VALUE;
91 }
92 snprintf(buffer, SIZE, "%*s- Source:%s\n", spaces + 4, "", sourceLiteral.c_str());
93 result.append(buffer);
94 }
95 snprintf(buffer, SIZE, "%*s- Uid:%d\n", spaces + 4, "", criterion.mValue.mUid);
96 result.append(buffer);
97 std::string ruleLiteral;
98 if (!RuleTypeConverter::toString(criterion.mRule, ruleLiteral)) {
99 ALOGE("%s: failed to convert source %d", __FUNCTION__,criterion.mRule);
100 return BAD_VALUE;
101 }
102 snprintf(buffer, SIZE, "%*s- Rule:%s\n", spaces + 4, "", ruleLiteral.c_str());
103 result.append(buffer);
104 }
105 write(fd, result.string(), result.size());
106 return NO_ERROR;
107 }
108
registerMix(const String8 & address,AudioMix mix,sp<SwAudioOutputDescriptor> desc)109 status_t AudioPolicyMixCollection::registerMix(const String8& address, AudioMix mix,
110 sp<SwAudioOutputDescriptor> desc)
111 {
112 ssize_t index = indexOfKey(address);
113 if (index >= 0) {
114 ALOGE("registerPolicyMixes(): mix for address %s already registered", address.string());
115 return BAD_VALUE;
116 }
117 sp<AudioPolicyMix> policyMix = new AudioPolicyMix(mix);
118 add(address, policyMix);
119
120 if (desc != 0) {
121 desc->mPolicyMix = policyMix;
122 policyMix->setOutput(desc);
123 }
124 return NO_ERROR;
125 }
126
unregisterMix(const String8 & address)127 status_t AudioPolicyMixCollection::unregisterMix(const String8& address)
128 {
129 ssize_t index = indexOfKey(address);
130 if (index < 0) {
131 ALOGE("unregisterPolicyMixes(): mix for address %s not registered", address.string());
132 return BAD_VALUE;
133 }
134
135 removeItemsAt(index);
136 return NO_ERROR;
137 }
138
getAudioPolicyMix(const String8 & address,sp<AudioPolicyMix> & policyMix) const139 status_t AudioPolicyMixCollection::getAudioPolicyMix(const String8& address,
140 sp<AudioPolicyMix> &policyMix) const
141 {
142 ssize_t index = indexOfKey(address);
143 if (index < 0) {
144 ALOGE("unregisterPolicyMixes(): mix for address %s not registered", address.string());
145 return BAD_VALUE;
146 }
147 policyMix = valueAt(index);
148 return NO_ERROR;
149 }
150
closeOutput(sp<SwAudioOutputDescriptor> & desc)151 void AudioPolicyMixCollection::closeOutput(sp<SwAudioOutputDescriptor> &desc)
152 {
153 for (size_t i = 0; i < size(); i++) {
154 sp<AudioPolicyMix> policyMix = valueAt(i);
155 if (policyMix->getOutput() == desc) {
156 policyMix->clearOutput();
157 }
158 }
159 }
160
getOutputForAttr(audio_attributes_t attributes,uid_t uid,sp<SwAudioOutputDescriptor> & desc)161 status_t AudioPolicyMixCollection::getOutputForAttr(audio_attributes_t attributes, uid_t uid,
162 sp<SwAudioOutputDescriptor> &desc)
163 {
164 ALOGV("getOutputForAttr() querying %zu mixes:", size());
165 desc = 0;
166 for (size_t i = 0; i < size(); i++) {
167 sp<AudioPolicyMix> mix = valueAt(i);
168
169 if (mix->mMixType == MIX_TYPE_PLAYERS) {
170 // TODO if adding more player rules (currently only 2), make rule handling "generic"
171 // as there is no difference in the treatment of usage- or uid-based rules
172 bool hasUsageMatchRules = false;
173 bool hasUsageExcludeRules = false;
174 bool usageMatchFound = false;
175 bool usageExclusionFound = false;
176
177 bool hasUidMatchRules = false;
178 bool hasUidExcludeRules = false;
179 bool uidMatchFound = false;
180 bool uidExclusionFound = false;
181
182 bool hasAddrMatch = false;
183
184 // iterate over all mix criteria to list what rules this mix contains
185 for (size_t j = 0; j < mix->mCriteria.size(); j++) {
186 ALOGV(" getOutputForAttr: mix %zu: inspecting mix criteria %zu of %zu",
187 i, j, mix->mCriteria.size());
188
189 // if there is an address match, prioritize that match
190 if (strncmp(attributes.tags, "addr=", strlen("addr=")) == 0 &&
191 strncmp(attributes.tags + strlen("addr="),
192 mix->mDeviceAddress.string(),
193 AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - strlen("addr=") - 1) == 0) {
194 hasAddrMatch = true;
195 break;
196 }
197
198 switch (mix->mCriteria[j].mRule) {
199 case RULE_MATCH_ATTRIBUTE_USAGE:
200 ALOGV("\tmix has RULE_MATCH_ATTRIBUTE_USAGE for usage %d",
201 mix->mCriteria[j].mValue.mUsage);
202 hasUsageMatchRules = true;
203 if (mix->mCriteria[j].mValue.mUsage == attributes.usage) {
204 // found one match against all allowed usages
205 usageMatchFound = true;
206 }
207 break;
208 case RULE_EXCLUDE_ATTRIBUTE_USAGE:
209 ALOGV("\tmix has RULE_EXCLUDE_ATTRIBUTE_USAGE for usage %d",
210 mix->mCriteria[j].mValue.mUsage);
211 hasUsageExcludeRules = true;
212 if (mix->mCriteria[j].mValue.mUsage == attributes.usage) {
213 // found this usage is to be excluded
214 usageExclusionFound = true;
215 }
216 break;
217 case RULE_MATCH_UID:
218 ALOGV("\tmix has RULE_MATCH_UID for uid %d", mix->mCriteria[j].mValue.mUid);
219 hasUidMatchRules = true;
220 if (mix->mCriteria[j].mValue.mUid == uid) {
221 // found one UID match against all allowed UIDs
222 uidMatchFound = true;
223 }
224 break;
225 case RULE_EXCLUDE_UID:
226 ALOGV("\tmix has RULE_EXCLUDE_UID for uid %d", mix->mCriteria[j].mValue.mUid);
227 hasUidExcludeRules = true;
228 if (mix->mCriteria[j].mValue.mUid == uid) {
229 // found this UID is to be excluded
230 uidExclusionFound = true;
231 }
232 break;
233 default:
234 break;
235 }
236
237 // consistency checks: for each "dimension" of rules (usage, uid...), we can
238 // only have MATCH rules, or EXCLUDE rules in each dimension, not a combination
239 if (hasUsageMatchRules && hasUsageExcludeRules) {
240 ALOGE("getOutputForAttr: invalid combination of RULE_MATCH_ATTRIBUTE_USAGE"
241 " and RULE_EXCLUDE_ATTRIBUTE_USAGE in mix %zu", i);
242 return BAD_VALUE;
243 }
244 if (hasUidMatchRules && hasUidExcludeRules) {
245 ALOGE("getOutputForAttr: invalid combination of RULE_MATCH_UID"
246 " and RULE_EXCLUDE_UID in mix %zu", i);
247 return BAD_VALUE;
248 }
249
250 if ((hasUsageExcludeRules && usageExclusionFound)
251 || (hasUidExcludeRules && uidExclusionFound)) {
252 break; // stop iterating on criteria because an exclusion was found (will fail)
253 }
254
255 }//iterate on mix criteria
256
257 // determine if exiting on success (or implicit failure as desc is 0)
258 if (hasAddrMatch ||
259 !((hasUsageExcludeRules && usageExclusionFound) ||
260 (hasUsageMatchRules && !usageMatchFound) ||
261 (hasUidExcludeRules && uidExclusionFound) ||
262 (hasUidMatchRules && !uidMatchFound))) {
263 ALOGV("\tgetOutputForAttr will use mix %zu", i);
264 desc = mix->getOutput();
265 }
266
267 } else if (mix->mMixType == MIX_TYPE_RECORDERS) {
268 if (attributes.usage == AUDIO_USAGE_VIRTUAL_SOURCE &&
269 strncmp(attributes.tags, "addr=", strlen("addr=")) == 0 &&
270 strncmp(attributes.tags + strlen("addr="),
271 mix->mDeviceAddress.string(),
272 AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - strlen("addr=") - 1) == 0) {
273 desc = mix->getOutput();
274 }
275 }
276 if (desc != 0) {
277 desc->mPolicyMix = mix;
278 return NO_ERROR;
279 }
280 }
281 return BAD_VALUE;
282 }
283
getDeviceAndMixForInputSource(audio_source_t inputSource,audio_devices_t availDevices,sp<AudioPolicyMix> * policyMix)284 audio_devices_t AudioPolicyMixCollection::getDeviceAndMixForInputSource(
285 audio_source_t inputSource,
286 audio_devices_t availDevices,
287 sp<AudioPolicyMix> *policyMix)
288 {
289 for (size_t i = 0; i < size(); i++) {
290 AudioPolicyMix *mix = valueAt(i).get();
291
292 if (mix->mMixType != MIX_TYPE_RECORDERS) {
293 continue;
294 }
295 for (size_t j = 0; j < mix->mCriteria.size(); j++) {
296 if ((RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET == mix->mCriteria[j].mRule &&
297 mix->mCriteria[j].mValue.mSource == inputSource) ||
298 (RULE_EXCLUDE_ATTRIBUTE_CAPTURE_PRESET == mix->mCriteria[j].mRule &&
299 mix->mCriteria[j].mValue.mSource != inputSource)) {
300 if (availDevices & AUDIO_DEVICE_IN_REMOTE_SUBMIX) {
301 if (policyMix != NULL) {
302 *policyMix = mix;
303 }
304 return AUDIO_DEVICE_IN_REMOTE_SUBMIX;
305 }
306 break;
307 }
308 }
309 }
310 return AUDIO_DEVICE_NONE;
311 }
312
getInputMixForAttr(audio_attributes_t attr,sp<AudioPolicyMix> * policyMix)313 status_t AudioPolicyMixCollection::getInputMixForAttr(
314 audio_attributes_t attr, sp<AudioPolicyMix> *policyMix)
315 {
316 if (strncmp(attr.tags, "addr=", strlen("addr=")) != 0) {
317 return BAD_VALUE;
318 }
319 String8 address(attr.tags + strlen("addr="));
320
321 #ifdef LOG_NDEBUG
322 ALOGV("getInputMixForAttr looking for address %s\n mixes available:", address.string());
323 for (size_t i = 0; i < size(); i++) {
324 sp<AudioPolicyMix> mix = valueAt(i);
325 ALOGV("\tmix %zu address=%s", i, mix->mDeviceAddress.string());
326 }
327 #endif
328
329 ssize_t index = indexOfKey(address);
330 if (index < 0) {
331 ALOGW("getInputMixForAttr() no policy for address %s", address.string());
332 return BAD_VALUE;
333 }
334 sp<AudioPolicyMix> audioPolicyMix = valueAt(index);
335
336 if (audioPolicyMix->mMixType != MIX_TYPE_PLAYERS) {
337 ALOGW("getInputMixForAttr() bad policy mix type for address %s", address.string());
338 return BAD_VALUE;
339 }
340 if (policyMix != nullptr) {
341 *policyMix = audioPolicyMix;
342 }
343 return NO_ERROR;
344 }
345
dump(int fd) const346 status_t AudioPolicyMixCollection::dump(int fd) const
347 {
348 std::string log("\nAudio Policy Mix:\n");
349 write(fd, log.c_str(), log.size());
350 for (size_t i = 0; i < size(); i++) {
351 valueAt(i)->dump(fd, 2, i);
352 }
353 return NO_ERROR;
354 }
355
356 }; //namespace android
357