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