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