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