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