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