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
28namespace android {
29
30void AudioPolicyMix::setOutput(sp<SwAudioOutputDescriptor> &output)
31{
32    mOutput = output;
33}
34
35const sp<SwAudioOutputDescriptor> &AudioPolicyMix::getOutput() const
36{
37    return mOutput;
38}
39
40void AudioPolicyMix::clearOutput()
41{
42    mOutput.clear();
43}
44
45void AudioPolicyMix::setMix(AudioMix &mix)
46{
47    mMix = mix;
48}
49
50android::AudioMix *AudioPolicyMix::getMix()
51{
52    return &mMix;
53}
54
55status_t AudioPolicyMix::dump(int fd, int spaces, int index) const
56{
57    const size_t SIZE = 256;
58    char buffer[SIZE];
59    String8 result;
60
61    snprintf(buffer, SIZE, "%*sAudio Policy Mix %d:\n", spaces, "", index+1);
62    result.append(buffer);
63    std::string mixTypeLiteral;
64    if (!MixTypeConverter::toString(mMix.mMixType, mixTypeLiteral)) {
65        ALOGE("%s: failed to convert mix type %d", __FUNCTION__, mMix.mMixType);
66        return BAD_VALUE;
67    }
68    snprintf(buffer, SIZE, "%*s- mix type: %s\n", spaces, "", mixTypeLiteral.c_str());
69    result.append(buffer);
70    std::string routeFlagLiteral;
71    RouteFlagTypeConverter::maskToString(mMix.mRouteFlags, routeFlagLiteral);
72    snprintf(buffer, SIZE, "%*s- Route Flags: %s\n", spaces, "", routeFlagLiteral.c_str());
73    result.append(buffer);
74    std::string deviceLiteral;
75    deviceToString(mMix.mDeviceType, deviceLiteral);
76    snprintf(buffer, SIZE, "%*s- device type: %s\n", spaces, "", deviceLiteral.c_str());
77    result.append(buffer);
78    snprintf(buffer, SIZE, "%*s- device address: %s\n", spaces, "", mMix.mDeviceAddress.string());
79    result.append(buffer);
80
81    int indexCriterion = 0;
82    for (const auto &criterion : mMix.mCriteria) {
83        snprintf(buffer, SIZE, "%*s- Criterion %d:\n", spaces + 2, "", indexCriterion++);
84        result.append(buffer);
85        std::string usageLiteral;
86        if (!UsageTypeConverter::toString(criterion.mValue.mUsage, usageLiteral)) {
87            ALOGE("%s: failed to convert usage %d", __FUNCTION__, criterion.mValue.mUsage);
88            return BAD_VALUE;
89        }
90        snprintf(buffer, SIZE, "%*s- Usage:%s\n", spaces + 4, "", usageLiteral.c_str());
91        result.append(buffer);
92        if (mMix.mMixType == MIX_TYPE_RECORDERS) {
93            std::string sourceLiteral;
94            if (!SourceTypeConverter::toString(criterion.mValue.mSource, sourceLiteral)) {
95                ALOGE("%s: failed to convert source %d", __FUNCTION__, criterion.mValue.mSource);
96                return BAD_VALUE;
97            }
98            snprintf(buffer, SIZE, "%*s- Source:%s\n", spaces + 4, "", sourceLiteral.c_str());
99            result.append(buffer);
100        }
101        snprintf(buffer, SIZE, "%*s- Uid:%d\n", spaces + 4, "", criterion.mValue.mUid);
102        result.append(buffer);
103        std::string ruleLiteral;
104        if (!RuleTypeConverter::toString(criterion.mRule, ruleLiteral)) {
105            ALOGE("%s: failed to convert source %d", __FUNCTION__,criterion.mRule);
106            return BAD_VALUE;
107        }
108        snprintf(buffer, SIZE, "%*s- Rule:%s\n", spaces + 4, "", ruleLiteral.c_str());
109        result.append(buffer);
110    }
111    write(fd, result.string(), result.size());
112    return NO_ERROR;
113}
114
115status_t AudioPolicyMixCollection::registerMix(const String8& address, AudioMix mix,
116                                               sp<SwAudioOutputDescriptor> desc)
117{
118    ssize_t index = indexOfKey(address);
119    if (index >= 0) {
120        ALOGE("registerPolicyMixes(): mix for address %s already registered", address.string());
121        return BAD_VALUE;
122    }
123    sp<AudioPolicyMix> policyMix = new AudioPolicyMix();
124    policyMix->setMix(mix);
125    add(address, policyMix);
126
127    if (desc != 0) {
128        desc->mPolicyMix = policyMix->getMix();
129        policyMix->setOutput(desc);
130    }
131    return NO_ERROR;
132}
133
134status_t AudioPolicyMixCollection::unregisterMix(const String8& address)
135{
136    ssize_t index = indexOfKey(address);
137    if (index < 0) {
138        ALOGE("unregisterPolicyMixes(): mix for address %s not registered", address.string());
139        return BAD_VALUE;
140    }
141
142    removeItemsAt(index);
143    return NO_ERROR;
144}
145
146status_t AudioPolicyMixCollection::getAudioPolicyMix(const String8& address,
147                                                     sp<AudioPolicyMix> &policyMix) const
148{
149    ssize_t index = indexOfKey(address);
150    if (index < 0) {
151        ALOGE("unregisterPolicyMixes(): mix for address %s not registered", address.string());
152        return BAD_VALUE;
153    }
154    policyMix = valueAt(index);
155    return NO_ERROR;
156}
157
158void AudioPolicyMixCollection::closeOutput(sp<SwAudioOutputDescriptor> &desc)
159{
160    for (size_t i = 0; i < size(); i++) {
161        sp<AudioPolicyMix> policyMix = valueAt(i);
162        if (policyMix->getOutput() == desc) {
163            policyMix->clearOutput();
164        }
165    }
166}
167
168status_t AudioPolicyMixCollection::getOutputForAttr(audio_attributes_t attributes, uid_t uid,
169                                                    sp<SwAudioOutputDescriptor> &desc)
170{
171    ALOGV("getOutputForAttr() querying %zu mixes:", size());
172    desc = 0;
173    for (size_t i = 0; i < size(); i++) {
174        sp<AudioPolicyMix> policyMix = valueAt(i);
175        AudioMix *mix = policyMix->getMix();
176
177        if (mix->mMixType == MIX_TYPE_PLAYERS) {
178            // TODO if adding more player rules (currently only 2), make rule handling "generic"
179            //      as there is no difference in the treatment of usage- or uid-based rules
180            bool hasUsageMatchRules = false;
181            bool hasUsageExcludeRules = false;
182            bool usageMatchFound = false;
183            bool usageExclusionFound = false;
184
185            bool hasUidMatchRules = false;
186            bool hasUidExcludeRules = false;
187            bool uidMatchFound = false;
188            bool uidExclusionFound = false;
189
190            bool hasAddrMatch = false;
191
192            // iterate over all mix criteria to list what rules this mix contains
193            for (size_t j = 0; j < mix->mCriteria.size(); j++) {
194                ALOGV(" getOutputForAttr: mix %zu: inspecting mix criteria %zu of %zu",
195                        i, j, mix->mCriteria.size());
196
197                // if there is an address match, prioritize that match
198                if (strncmp(attributes.tags, "addr=", strlen("addr=")) == 0 &&
199                        strncmp(attributes.tags + strlen("addr="),
200                                mix->mDeviceAddress.string(),
201                                AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - strlen("addr=") - 1) == 0) {
202                    hasAddrMatch = true;
203                    break;
204                }
205
206                switch (mix->mCriteria[j].mRule) {
207                case RULE_MATCH_ATTRIBUTE_USAGE:
208                    ALOGV("\tmix has RULE_MATCH_ATTRIBUTE_USAGE for usage %d",
209                                                mix->mCriteria[j].mValue.mUsage);
210                    hasUsageMatchRules = true;
211                    if (mix->mCriteria[j].mValue.mUsage == attributes.usage) {
212                        // found one match against all allowed usages
213                        usageMatchFound = true;
214                    }
215                    break;
216                case RULE_EXCLUDE_ATTRIBUTE_USAGE:
217                    ALOGV("\tmix has RULE_EXCLUDE_ATTRIBUTE_USAGE for usage %d",
218                            mix->mCriteria[j].mValue.mUsage);
219                    hasUsageExcludeRules = true;
220                    if (mix->mCriteria[j].mValue.mUsage == attributes.usage) {
221                        // found this usage is to be excluded
222                        usageExclusionFound = true;
223                    }
224                    break;
225                case RULE_MATCH_UID:
226                    ALOGV("\tmix has RULE_MATCH_UID for uid %d", mix->mCriteria[j].mValue.mUid);
227                    hasUidMatchRules = true;
228                    if (mix->mCriteria[j].mValue.mUid == uid) {
229                        // found one UID match against all allowed UIDs
230                        uidMatchFound = true;
231                    }
232                    break;
233                case RULE_EXCLUDE_UID:
234                    ALOGV("\tmix has RULE_EXCLUDE_UID for uid %d", mix->mCriteria[j].mValue.mUid);
235                    hasUidExcludeRules = true;
236                    if (mix->mCriteria[j].mValue.mUid == uid) {
237                        // found this UID is to be excluded
238                        uidExclusionFound = true;
239                    }
240                    break;
241                default:
242                    break;
243                }
244
245                // consistency checks: for each "dimension" of rules (usage, uid...), we can
246                // only have MATCH rules, or EXCLUDE rules in each dimension, not a combination
247                if (hasUsageMatchRules && hasUsageExcludeRules) {
248                    ALOGE("getOutputForAttr: invalid combination of RULE_MATCH_ATTRIBUTE_USAGE"
249                            " and RULE_EXCLUDE_ATTRIBUTE_USAGE in mix %zu", i);
250                    return BAD_VALUE;
251                }
252                if (hasUidMatchRules && hasUidExcludeRules) {
253                    ALOGE("getOutputForAttr: invalid combination of RULE_MATCH_UID"
254                            " and RULE_EXCLUDE_UID in mix %zu", i);
255                    return BAD_VALUE;
256                }
257
258                if ((hasUsageExcludeRules && usageExclusionFound)
259                        || (hasUidExcludeRules && uidExclusionFound)) {
260                    break; // stop iterating on criteria because an exclusion was found (will fail)
261                }
262
263            }//iterate on mix criteria
264
265            // determine if exiting on success (or implicit failure as desc is 0)
266            if (hasAddrMatch ||
267                    !((hasUsageExcludeRules && usageExclusionFound) ||
268                      (hasUsageMatchRules && !usageMatchFound)  ||
269                      (hasUidExcludeRules && uidExclusionFound) ||
270                      (hasUidMatchRules && !uidMatchFound))) {
271                ALOGV("\tgetOutputForAttr will use mix %zu", i);
272                desc = policyMix->getOutput();
273            }
274
275        } else if (mix->mMixType == MIX_TYPE_RECORDERS) {
276            if (attributes.usage == AUDIO_USAGE_VIRTUAL_SOURCE &&
277                    strncmp(attributes.tags, "addr=", strlen("addr=")) == 0 &&
278                    strncmp(attributes.tags + strlen("addr="),
279                            mix->mDeviceAddress.string(),
280                            AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - strlen("addr=") - 1) == 0) {
281                desc = policyMix->getOutput();
282            }
283        }
284        if (desc != 0) {
285            desc->mPolicyMix = mix;
286            return NO_ERROR;
287        }
288    }
289    return BAD_VALUE;
290}
291
292audio_devices_t AudioPolicyMixCollection::getDeviceAndMixForInputSource(audio_source_t inputSource,
293                                                                        audio_devices_t availDevices,
294                                                                        AudioMix **policyMix)
295{
296    for (size_t i = 0; i < size(); i++) {
297        AudioMix *mix = valueAt(i)->getMix();
298
299        if (mix->mMixType != MIX_TYPE_RECORDERS) {
300            continue;
301        }
302        for (size_t j = 0; j < mix->mCriteria.size(); j++) {
303            if ((RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET == mix->mCriteria[j].mRule &&
304                    mix->mCriteria[j].mValue.mSource == inputSource) ||
305               (RULE_EXCLUDE_ATTRIBUTE_CAPTURE_PRESET == mix->mCriteria[j].mRule &&
306                    mix->mCriteria[j].mValue.mSource != inputSource)) {
307                if (availDevices & AUDIO_DEVICE_IN_REMOTE_SUBMIX) {
308                    if (policyMix != NULL) {
309                        *policyMix = mix;
310                    }
311                    return AUDIO_DEVICE_IN_REMOTE_SUBMIX;
312                }
313                break;
314            }
315        }
316    }
317    return AUDIO_DEVICE_NONE;
318}
319
320status_t AudioPolicyMixCollection::getInputMixForAttr(audio_attributes_t attr, AudioMix **policyMix)
321{
322    if (strncmp(attr.tags, "addr=", strlen("addr=")) != 0) {
323        return BAD_VALUE;
324    }
325    String8 address(attr.tags + strlen("addr="));
326
327#ifdef LOG_NDEBUG
328    ALOGV("getInputMixForAttr looking for address %s\n  mixes available:", address.string());
329    for (size_t i = 0; i < size(); i++) {
330            sp<AudioPolicyMix> policyMix = valueAt(i);
331            AudioMix *mix = policyMix->getMix();
332            ALOGV("\tmix %zu address=%s", i, mix->mDeviceAddress.string());
333    }
334#endif
335
336    ssize_t index = indexOfKey(address);
337    if (index < 0) {
338        ALOGW("getInputMixForAttr() no policy for address %s", address.string());
339        return BAD_VALUE;
340    }
341    sp<AudioPolicyMix> audioPolicyMix = valueAt(index);
342    AudioMix *mix = audioPolicyMix->getMix();
343
344    if (mix->mMixType != MIX_TYPE_PLAYERS) {
345        ALOGW("getInputMixForAttr() bad policy mix type for address %s", address.string());
346        return BAD_VALUE;
347    }
348    *policyMix = mix;
349    return NO_ERROR;
350}
351
352status_t AudioPolicyMixCollection::dump(int fd) const
353{
354    std::string log("\nAudio Policy Mix:\n");
355    write(fd, log.c_str(), log.size());
356    for (size_t i = 0; i < size(); i++) {
357        valueAt(i)->dump(fd, 2, i);
358    }
359    return NO_ERROR;
360}
361
362}; //namespace android
363