AudioPolicyMix.cpp revision e8decedb429ed76dfa84cdb3e80ab3b969e77298
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
27namespace android {
28
29void AudioPolicyMix::setOutput(sp<SwAudioOutputDescriptor> &output)
30{
31    mOutput = output;
32}
33
34const sp<SwAudioOutputDescriptor> &AudioPolicyMix::getOutput() const
35{
36    return mOutput;
37}
38
39void AudioPolicyMix::clearOutput()
40{
41    mOutput.clear();
42}
43
44void AudioPolicyMix::setMix(AudioMix &mix)
45{
46    mMix = mix;
47}
48
49android::AudioMix *AudioPolicyMix::getMix()
50{
51    return &mMix;
52}
53
54status_t AudioPolicyMixCollection::registerMix(String8 address, AudioMix mix)
55{
56    ssize_t index = indexOfKey(address);
57    if (index >= 0) {
58        ALOGE("registerPolicyMixes(): mix for address %s already registered", address.string());
59        return BAD_VALUE;
60    }
61    sp<AudioPolicyMix> policyMix = new AudioPolicyMix();
62    policyMix->setMix(mix);
63    add(address, policyMix);
64    return NO_ERROR;
65}
66
67status_t AudioPolicyMixCollection::unregisterMix(String8 address)
68{
69    ssize_t index = indexOfKey(address);
70    if (index < 0) {
71        ALOGE("unregisterPolicyMixes(): mix for address %s not registered", address.string());
72        return BAD_VALUE;
73    }
74
75    removeItemsAt(index);
76    return NO_ERROR;
77}
78
79status_t AudioPolicyMixCollection::getAudioPolicyMix(String8 address,
80                                                     sp<AudioPolicyMix> &policyMix) const
81{
82    ssize_t index = indexOfKey(address);
83    if (index < 0) {
84        ALOGE("unregisterPolicyMixes(): mix for address %s not registered", address.string());
85        return BAD_VALUE;
86    }
87    policyMix = valueAt(index);
88    return NO_ERROR;
89}
90
91void AudioPolicyMixCollection::closeOutput(sp<SwAudioOutputDescriptor> &desc)
92{
93    for (size_t i = 0; i < size(); i++) {
94        sp<AudioPolicyMix> policyMix = valueAt(i);
95        if (policyMix->getOutput() == desc) {
96            policyMix->clearOutput();
97        }
98    }
99}
100
101status_t AudioPolicyMixCollection::getOutputForAttr(audio_attributes_t attributes, uid_t uid,
102                                                    sp<SwAudioOutputDescriptor> &desc)
103{
104    desc = 0;
105    for (size_t i = 0; i < size(); i++) {
106        sp<AudioPolicyMix> policyMix = valueAt(i);
107        AudioMix *mix = policyMix->getMix();
108
109        if (mix->mMixType == MIX_TYPE_PLAYERS) {
110            // TODO if adding more player rules (currently only 2), make rule handling "generic"
111            //      as there is no difference in the treatment of usage- or uid-based rules
112            bool hasUsageMatchRules = false;
113            bool hasUsageExcludeRules = false;
114            bool usageMatchFound = false;
115            bool usageExclusionFound = false;
116
117            bool hasUidMatchRules = false;
118            bool hasUidExcludeRules = false;
119            bool uidMatchFound = false;
120            bool uidExclusionFound = false;
121
122            bool hasAddrMatch = false;
123
124            // iterate over all mix criteria to list what rules this mix contains
125            for (size_t j = 0; j < mix->mCriteria.size(); j++) {
126                ALOGV("getOutputForAttr: inspecting mix %zu of %zu", i, mix->mCriteria.size());
127
128                // if there is an address match, prioritize that match
129                if (strncmp(attributes.tags, "addr=", strlen("addr=")) == 0 &&
130                        strncmp(attributes.tags + strlen("addr="),
131                                mix->mRegistrationId.string(),
132                                AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - strlen("addr=") - 1) == 0) {
133                    hasAddrMatch = true;
134                    break;
135                }
136
137                switch (mix->mCriteria[j].mRule) {
138                case RULE_MATCH_ATTRIBUTE_USAGE:
139                    ALOGV("\tmix has RULE_MATCH_ATTRIBUTE_USAGE for usage %d",
140                                                mix->mCriteria[j].mValue.mUsage);
141                    hasUsageMatchRules = true;
142                    if (mix->mCriteria[j].mValue.mUsage == attributes.usage) {
143                        // found one match against all allowed usages
144                        usageMatchFound = true;
145                    }
146                    break;
147                case RULE_EXCLUDE_ATTRIBUTE_USAGE:
148                    ALOGV("\tmix has RULE_EXCLUDE_ATTRIBUTE_USAGE for usage %d",
149                            mix->mCriteria[j].mValue.mUsage);
150                    hasUsageExcludeRules = true;
151                    if (mix->mCriteria[j].mValue.mUsage == attributes.usage) {
152                        // found this usage is to be excluded
153                        usageExclusionFound = true;
154                    }
155                    break;
156                case RULE_MATCH_UID:
157                    ALOGV("\tmix has RULE_MATCH_UID for uid %d", mix->mCriteria[j].mValue.mUid);
158                    hasUidMatchRules = true;
159                    if (mix->mCriteria[j].mValue.mUid == uid) {
160                        // found one UID match against all allowed UIDs
161                        uidMatchFound = true;
162                    }
163                    break;
164                case RULE_EXCLUDE_UID:
165                    ALOGV("\tmix has RULE_EXCLUDE_UID for uid %d", mix->mCriteria[j].mValue.mUid);
166                    hasUidExcludeRules = true;
167                    if (mix->mCriteria[j].mValue.mUid == uid) {
168                        // found this UID is to be excluded
169                        uidExclusionFound = true;
170                    }
171                    break;
172                default:
173                    break;
174                }
175
176                // consistency checks: for each "dimension" of rules (usage, uid...), we can
177                // only have MATCH rules, or EXCLUDE rules in each dimension, not a combination
178                if (hasUsageMatchRules && hasUsageExcludeRules) {
179                    ALOGE("getOutputForAttr: invalid combination of RULE_MATCH_ATTRIBUTE_USAGE"
180                            " and RULE_EXCLUDE_ATTRIBUTE_USAGE in mix %zu", i);
181                    return BAD_VALUE;
182                }
183                if (hasUidMatchRules && hasUidExcludeRules) {
184                    ALOGE("getOutputForAttr: invalid combination of RULE_MATCH_UID"
185                            " and RULE_EXCLUDE_UID in mix %zu", i);
186                    return BAD_VALUE;
187                }
188
189                if ((hasUsageExcludeRules && usageExclusionFound)
190                        || (hasUidExcludeRules && uidExclusionFound)) {
191                    break; // stop iterating on criteria because an exclusion was found (will fail)
192                }
193
194            }//iterate on mix criteria
195
196            // determine if exiting on success (or implicit failure as desc is 0)
197            if (hasAddrMatch ||
198                    !((hasUsageExcludeRules && usageExclusionFound) ||
199                      (hasUsageMatchRules && !usageMatchFound)  ||
200                      (hasUidExcludeRules && uidExclusionFound) ||
201                      (hasUidMatchRules && !uidMatchFound))) {
202                ALOGV("\tgetOutputForAttr will use mix %zu", i);
203                desc = policyMix->getOutput();
204            }
205
206        } else if (mix->mMixType == MIX_TYPE_RECORDERS) {
207            if (attributes.usage == AUDIO_USAGE_VIRTUAL_SOURCE &&
208                    strncmp(attributes.tags, "addr=", strlen("addr=")) == 0 &&
209                    strncmp(attributes.tags + strlen("addr="),
210                            mix->mRegistrationId.string(),
211                            AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - strlen("addr=") - 1) == 0) {
212                desc = policyMix->getOutput();
213            }
214        }
215        if (desc != 0) {
216            desc->mPolicyMix = mix;
217            return NO_ERROR;
218        }
219    }
220    return BAD_VALUE;
221}
222
223audio_devices_t AudioPolicyMixCollection::getDeviceAndMixForInputSource(audio_source_t inputSource,
224                                                                        audio_devices_t availDevices,
225                                                                        AudioMix **policyMix)
226{
227    for (size_t i = 0; i < size(); i++) {
228        AudioMix *mix = valueAt(i)->getMix();
229
230        if (mix->mMixType != MIX_TYPE_RECORDERS) {
231            continue;
232        }
233        for (size_t j = 0; j < mix->mCriteria.size(); j++) {
234            if ((RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET == mix->mCriteria[j].mRule &&
235                    mix->mCriteria[j].mValue.mSource == inputSource) ||
236               (RULE_EXCLUDE_ATTRIBUTE_CAPTURE_PRESET == mix->mCriteria[j].mRule &&
237                    mix->mCriteria[j].mValue.mSource != inputSource)) {
238                if (availDevices & AUDIO_DEVICE_IN_REMOTE_SUBMIX) {
239                    if (policyMix != NULL) {
240                        *policyMix = mix;
241                    }
242                    return AUDIO_DEVICE_IN_REMOTE_SUBMIX;
243                }
244                break;
245            }
246        }
247    }
248    return AUDIO_DEVICE_NONE;
249}
250
251status_t AudioPolicyMixCollection::getInputMixForAttr(audio_attributes_t attr, AudioMix **policyMix)
252{
253    if (strncmp(attr.tags, "addr=", strlen("addr=")) != 0) {
254        return BAD_VALUE;
255    }
256    String8 address(attr.tags + strlen("addr="));
257
258#ifdef LOG_NDEBUG
259    ALOGV("getInputMixForAttr looking for address %s\n  mixes available:", address.string());
260    for (size_t i = 0; i < size(); i++) {
261            sp<AudioPolicyMix> policyMix = valueAt(i);
262            AudioMix *mix = policyMix->getMix();
263            ALOGV("\tmix %zu address=%s", i, mix->mRegistrationId.string());
264    }
265#endif
266
267    ssize_t index = indexOfKey(address);
268    if (index < 0) {
269        ALOGW("getInputMixForAttr() no policy for address %s", address.string());
270        return BAD_VALUE;
271    }
272    sp<AudioPolicyMix> audioPolicyMix = valueAt(index);
273    AudioMix *mix = audioPolicyMix->getMix();
274
275    if (mix->mMixType != MIX_TYPE_PLAYERS) {
276        ALOGW("getInputMixForAttr() bad policy mix type for address %s", address.string());
277        return BAD_VALUE;
278    }
279    *policyMix = mix;
280    return NO_ERROR;
281}
282
283}; //namespace android
284