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    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
231audio_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
259status_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