AudioPolicyMix.cpp revision 7638ca29e8400a19524adb982e9d22c02786de82
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                                               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
73status_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
85status_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
97void 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
107status_t AudioPolicyMixCollection::getOutputForAttr(audio_attributes_t attributes, uid_t uid,
108                                                    sp<SwAudioOutputDescriptor> &desc)
109{
110    desc = 0;
111    for (size_t i = 0; i < size(); i++) {
112        sp<AudioPolicyMix> policyMix = valueAt(i);
113        AudioMix *mix = policyMix->getMix();
114
115        if (mix->mMixType == MIX_TYPE_PLAYERS) {
116            // TODO if adding more player rules (currently only 2), make rule handling "generic"
117            //      as there is no difference in the treatment of usage- or uid-based rules
118            bool hasUsageMatchRules = false;
119            bool hasUsageExcludeRules = false;
120            bool usageMatchFound = false;
121            bool usageExclusionFound = false;
122
123            bool hasUidMatchRules = false;
124            bool hasUidExcludeRules = false;
125            bool uidMatchFound = false;
126            bool uidExclusionFound = false;
127
128            bool hasAddrMatch = false;
129
130            // iterate over all mix criteria to list what rules this mix contains
131            for (size_t j = 0; j < mix->mCriteria.size(); j++) {
132                ALOGV("getOutputForAttr: inspecting mix %zu of %zu", i, mix->mCriteria.size());
133
134                // if there is an address match, prioritize that match
135                if (strncmp(attributes.tags, "addr=", strlen("addr=")) == 0 &&
136                        strncmp(attributes.tags + strlen("addr="),
137                                mix->mDeviceAddress.string(),
138                                AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - strlen("addr=") - 1) == 0) {
139                    hasAddrMatch = true;
140                    break;
141                }
142
143                switch (mix->mCriteria[j].mRule) {
144                case RULE_MATCH_ATTRIBUTE_USAGE:
145                    ALOGV("\tmix has RULE_MATCH_ATTRIBUTE_USAGE for usage %d",
146                                                mix->mCriteria[j].mValue.mUsage);
147                    hasUsageMatchRules = true;
148                    if (mix->mCriteria[j].mValue.mUsage == attributes.usage) {
149                        // found one match against all allowed usages
150                        usageMatchFound = true;
151                    }
152                    break;
153                case RULE_EXCLUDE_ATTRIBUTE_USAGE:
154                    ALOGV("\tmix has RULE_EXCLUDE_ATTRIBUTE_USAGE for usage %d",
155                            mix->mCriteria[j].mValue.mUsage);
156                    hasUsageExcludeRules = true;
157                    if (mix->mCriteria[j].mValue.mUsage == attributes.usage) {
158                        // found this usage is to be excluded
159                        usageExclusionFound = true;
160                    }
161                    break;
162                case RULE_MATCH_UID:
163                    ALOGV("\tmix has RULE_MATCH_UID for uid %d", mix->mCriteria[j].mValue.mUid);
164                    hasUidMatchRules = true;
165                    if (mix->mCriteria[j].mValue.mUid == uid) {
166                        // found one UID match against all allowed UIDs
167                        uidMatchFound = true;
168                    }
169                    break;
170                case RULE_EXCLUDE_UID:
171                    ALOGV("\tmix has RULE_EXCLUDE_UID for uid %d", mix->mCriteria[j].mValue.mUid);
172                    hasUidExcludeRules = true;
173                    if (mix->mCriteria[j].mValue.mUid == uid) {
174                        // found this UID is to be excluded
175                        uidExclusionFound = true;
176                    }
177                    break;
178                default:
179                    break;
180                }
181
182                // consistency checks: for each "dimension" of rules (usage, uid...), we can
183                // only have MATCH rules, or EXCLUDE rules in each dimension, not a combination
184                if (hasUsageMatchRules && hasUsageExcludeRules) {
185                    ALOGE("getOutputForAttr: invalid combination of RULE_MATCH_ATTRIBUTE_USAGE"
186                            " and RULE_EXCLUDE_ATTRIBUTE_USAGE in mix %zu", i);
187                    return BAD_VALUE;
188                }
189                if (hasUidMatchRules && hasUidExcludeRules) {
190                    ALOGE("getOutputForAttr: invalid combination of RULE_MATCH_UID"
191                            " and RULE_EXCLUDE_UID in mix %zu", i);
192                    return BAD_VALUE;
193                }
194
195                if ((hasUsageExcludeRules && usageExclusionFound)
196                        || (hasUidExcludeRules && uidExclusionFound)) {
197                    break; // stop iterating on criteria because an exclusion was found (will fail)
198                }
199
200            }//iterate on mix criteria
201
202            // determine if exiting on success (or implicit failure as desc is 0)
203            if (hasAddrMatch ||
204                    !((hasUsageExcludeRules && usageExclusionFound) ||
205                      (hasUsageMatchRules && !usageMatchFound)  ||
206                      (hasUidExcludeRules && uidExclusionFound) ||
207                      (hasUidMatchRules && !uidMatchFound))) {
208                ALOGV("\tgetOutputForAttr will use mix %zu", i);
209                desc = policyMix->getOutput();
210            }
211
212        } else if (mix->mMixType == MIX_TYPE_RECORDERS) {
213            if (attributes.usage == AUDIO_USAGE_VIRTUAL_SOURCE &&
214                    strncmp(attributes.tags, "addr=", strlen("addr=")) == 0 &&
215                    strncmp(attributes.tags + strlen("addr="),
216                            mix->mDeviceAddress.string(),
217                            AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - strlen("addr=") - 1) == 0) {
218                desc = policyMix->getOutput();
219            }
220        }
221        if (desc != 0) {
222            desc->mPolicyMix = mix;
223            return NO_ERROR;
224        }
225    }
226    return BAD_VALUE;
227}
228
229audio_devices_t AudioPolicyMixCollection::getDeviceAndMixForInputSource(audio_source_t inputSource,
230                                                                        audio_devices_t availDevices,
231                                                                        AudioMix **policyMix)
232{
233    for (size_t i = 0; i < size(); i++) {
234        AudioMix *mix = valueAt(i)->getMix();
235
236        if (mix->mMixType != MIX_TYPE_RECORDERS) {
237            continue;
238        }
239        for (size_t j = 0; j < mix->mCriteria.size(); j++) {
240            if ((RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET == mix->mCriteria[j].mRule &&
241                    mix->mCriteria[j].mValue.mSource == inputSource) ||
242               (RULE_EXCLUDE_ATTRIBUTE_CAPTURE_PRESET == mix->mCriteria[j].mRule &&
243                    mix->mCriteria[j].mValue.mSource != inputSource)) {
244                if (availDevices & AUDIO_DEVICE_IN_REMOTE_SUBMIX) {
245                    if (policyMix != NULL) {
246                        *policyMix = mix;
247                    }
248                    return AUDIO_DEVICE_IN_REMOTE_SUBMIX;
249                }
250                break;
251            }
252        }
253    }
254    return AUDIO_DEVICE_NONE;
255}
256
257status_t AudioPolicyMixCollection::getInputMixForAttr(audio_attributes_t attr, AudioMix **policyMix)
258{
259    if (strncmp(attr.tags, "addr=", strlen("addr=")) != 0) {
260        return BAD_VALUE;
261    }
262    String8 address(attr.tags + strlen("addr="));
263
264#ifdef LOG_NDEBUG
265    ALOGV("getInputMixForAttr looking for address %s\n  mixes available:", address.string());
266    for (size_t i = 0; i < size(); i++) {
267            sp<AudioPolicyMix> policyMix = valueAt(i);
268            AudioMix *mix = policyMix->getMix();
269            ALOGV("\tmix %zu address=%s", i, mix->mDeviceAddress.string());
270    }
271#endif
272
273    ssize_t index = indexOfKey(address);
274    if (index < 0) {
275        ALOGW("getInputMixForAttr() no policy for address %s", address.string());
276        return BAD_VALUE;
277    }
278    sp<AudioPolicyMix> audioPolicyMix = valueAt(index);
279    AudioMix *mix = audioPolicyMix->getMix();
280
281    if (mix->mMixType != MIX_TYPE_PLAYERS) {
282        ALOGW("getInputMixForAttr() bad policy mix type for address %s", address.string());
283        return BAD_VALUE;
284    }
285    *policyMix = mix;
286    return NO_ERROR;
287}
288
289}; //namespace android
290