Home | History | Annotate | Download | only in src
      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 
     29 void AudioPolicyMix::setOutput(sp<SwAudioOutputDescriptor> &output)
     30 {
     31     mOutput = output;
     32 }
     33 
     34 const sp<SwAudioOutputDescriptor> &AudioPolicyMix::getOutput() const
     35 {
     36     return mOutput;
     37 }
     38 
     39 void AudioPolicyMix::clearOutput()
     40 {
     41     mOutput.clear();
     42 }
     43 
     44 void AudioPolicyMix::setMix(AudioMix &mix)
     45 {
     46     mMix = mix;
     47 }
     48 
     49 android::AudioMix *AudioPolicyMix::getMix()
     50 {
     51     return &mMix;
     52 }
     53 
     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 
     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 
     85 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 
     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 
    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 
    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 
    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