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