• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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