1 /* 2 * Copyright (C) 2014 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 package android.media.audiopolicy; 18 19 import android.annotation.NonNull; 20 import android.annotation.SystemApi; 21 import android.compat.annotation.UnsupportedAppUsage; 22 import android.media.AudioAttributes; 23 import android.os.Build; 24 import android.os.Parcel; 25 import android.util.Log; 26 27 import java.util.ArrayList; 28 import java.util.Iterator; 29 import java.util.Objects; 30 31 32 /** 33 * @hide 34 * 35 * Here's an example of creating a mixing rule for all media playback: 36 * <pre> 37 * AudioAttributes mediaAttr = new AudioAttributes.Builder() 38 * .setUsage(AudioAttributes.USAGE_MEDIA) 39 * .build(); 40 * AudioMixingRule mediaRule = new AudioMixingRule.Builder() 41 * .addRule(mediaAttr, AudioMixingRule.RULE_MATCH_ATTRIBUTE_USAGE) 42 * .build(); 43 * </pre> 44 */ 45 @SystemApi 46 public class AudioMixingRule { 47 AudioMixingRule(int mixType, ArrayList<AudioMixMatchCriterion> criteria, boolean allowPrivilegedMediaPlaybackCapture, boolean voiceCommunicationCaptureAllowed)48 private AudioMixingRule(int mixType, ArrayList<AudioMixMatchCriterion> criteria, 49 boolean allowPrivilegedMediaPlaybackCapture, 50 boolean voiceCommunicationCaptureAllowed) { 51 mCriteria = criteria; 52 mTargetMixType = mixType; 53 mAllowPrivilegedPlaybackCapture = allowPrivilegedMediaPlaybackCapture; 54 mVoiceCommunicationCaptureAllowed = voiceCommunicationCaptureAllowed; 55 } 56 57 /** 58 * A rule requiring the usage information of the {@link AudioAttributes} to match. 59 * This mixing rule can be added with {@link Builder#addRule(AudioAttributes, int)} or 60 * {@link Builder#addMixRule(int, Object)} where the Object parameter is an instance of 61 * {@link AudioAttributes}. 62 */ 63 public static final int RULE_MATCH_ATTRIBUTE_USAGE = 0x1; 64 /** 65 * A rule requiring the capture preset information of the {@link AudioAttributes} to match. 66 * This mixing rule can be added with {@link Builder#addRule(AudioAttributes, int)} or 67 * {@link Builder#addMixRule(int, Object)} where the Object parameter is an instance of 68 * {@link AudioAttributes}. 69 */ 70 public static final int RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET = 0x1 << 1; 71 /** 72 * A rule requiring the UID of the audio stream to match that specified. 73 * This mixing rule can be added with {@link Builder#addMixRule(int, Object)} where the Object 74 * parameter is an instance of {@link java.lang.Integer}. 75 */ 76 public static final int RULE_MATCH_UID = 0x1 << 2; 77 /** 78 * A rule requiring the userId of the audio stream to match that specified. 79 * This mixing rule can be added with {@link Builder#addMixRule(int, Object)} where the Object 80 * parameter is an instance of {@link java.lang.Integer}. 81 */ 82 public static final int RULE_MATCH_USERID = 0x1 << 3; 83 84 private final static int RULE_EXCLUSION_MASK = 0x8000; 85 /** 86 * @hide 87 * A rule requiring the usage information of the {@link AudioAttributes} to differ. 88 */ 89 public static final int RULE_EXCLUDE_ATTRIBUTE_USAGE = 90 RULE_EXCLUSION_MASK | RULE_MATCH_ATTRIBUTE_USAGE; 91 /** 92 * @hide 93 * A rule requiring the capture preset information of the {@link AudioAttributes} to differ. 94 */ 95 public static final int RULE_EXCLUDE_ATTRIBUTE_CAPTURE_PRESET = 96 RULE_EXCLUSION_MASK | RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET; 97 /** 98 * @hide 99 * A rule requiring the UID information to differ. 100 */ 101 public static final int RULE_EXCLUDE_UID = 102 RULE_EXCLUSION_MASK | RULE_MATCH_UID; 103 104 /** 105 * @hide 106 * A rule requiring the userId information to differ. 107 */ 108 public static final int RULE_EXCLUDE_USERID = 109 RULE_EXCLUSION_MASK | RULE_MATCH_USERID; 110 111 /** @hide */ 112 public static final class AudioMixMatchCriterion { 113 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 114 final AudioAttributes mAttr; 115 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 116 final int mIntProp; 117 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 118 final int mRule; 119 120 /** input parameters must be valid */ AudioMixMatchCriterion(AudioAttributes attributes, int rule)121 AudioMixMatchCriterion(AudioAttributes attributes, int rule) { 122 mAttr = attributes; 123 mIntProp = Integer.MIN_VALUE; 124 mRule = rule; 125 } 126 /** input parameters must be valid */ AudioMixMatchCriterion(Integer intProp, int rule)127 AudioMixMatchCriterion(Integer intProp, int rule) { 128 mAttr = null; 129 mIntProp = intProp.intValue(); 130 mRule = rule; 131 } 132 133 @Override hashCode()134 public int hashCode() { 135 return Objects.hash(mAttr, mIntProp, mRule); 136 } 137 writeToParcel(Parcel dest)138 void writeToParcel(Parcel dest) { 139 dest.writeInt(mRule); 140 final int match_rule = mRule & ~RULE_EXCLUSION_MASK; 141 switch (match_rule) { 142 case RULE_MATCH_ATTRIBUTE_USAGE: 143 dest.writeInt(mAttr.getSystemUsage()); 144 break; 145 case RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET: 146 dest.writeInt(mAttr.getCapturePreset()); 147 break; 148 case RULE_MATCH_UID: 149 case RULE_MATCH_USERID: 150 dest.writeInt(mIntProp); 151 break; 152 default: 153 Log.e("AudioMixMatchCriterion", "Unknown match rule" + match_rule 154 + " when writing to Parcel"); 155 dest.writeInt(-1); 156 } 157 } 158 getAudioAttributes()159 public AudioAttributes getAudioAttributes() { return mAttr; } getIntProp()160 public int getIntProp() { return mIntProp; } getRule()161 public int getRule() { return mRule; } 162 } 163 isAffectingUsage(int usage)164 boolean isAffectingUsage(int usage) { 165 for (AudioMixMatchCriterion criterion : mCriteria) { 166 if ((criterion.mRule & RULE_MATCH_ATTRIBUTE_USAGE) != 0 167 && criterion.mAttr != null 168 && criterion.mAttr.getSystemUsage() == usage) { 169 return true; 170 } 171 } 172 return false; 173 } 174 175 /** 176 * Returns {@code true} if this rule contains a RULE_MATCH_ATTRIBUTE_USAGE criterion for 177 * the given usage 178 * 179 * @hide 180 */ containsMatchAttributeRuleForUsage(int usage)181 boolean containsMatchAttributeRuleForUsage(int usage) { 182 for (AudioMixMatchCriterion criterion : mCriteria) { 183 if (criterion.mRule == RULE_MATCH_ATTRIBUTE_USAGE 184 && criterion.mAttr != null 185 && criterion.mAttr.getSystemUsage() == usage) { 186 return true; 187 } 188 } 189 return false; 190 } 191 areCriteriaEquivalent(ArrayList<AudioMixMatchCriterion> cr1, ArrayList<AudioMixMatchCriterion> cr2)192 private static boolean areCriteriaEquivalent(ArrayList<AudioMixMatchCriterion> cr1, 193 ArrayList<AudioMixMatchCriterion> cr2) { 194 if (cr1 == null || cr2 == null) return false; 195 if (cr1 == cr2) return true; 196 if (cr1.size() != cr2.size()) return false; 197 //TODO iterate over rules to check they contain the same criterion 198 return (cr1.hashCode() == cr2.hashCode()); 199 } 200 201 private final int mTargetMixType; getTargetMixType()202 int getTargetMixType() { return mTargetMixType; } 203 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 204 private final ArrayList<AudioMixMatchCriterion> mCriteria; 205 /** @hide */ getCriteria()206 public ArrayList<AudioMixMatchCriterion> getCriteria() { return mCriteria; } 207 /** Indicates that this rule is intended to capture media or game playback by a system component 208 * with permission CAPTURE_MEDIA_OUTPUT or CAPTURE_AUDIO_OUTPUT. 209 */ 210 //TODO b/177061175: rename to mAllowPrivilegedMediaPlaybackCapture 211 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 212 private boolean mAllowPrivilegedPlaybackCapture = false; 213 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 214 private boolean mVoiceCommunicationCaptureAllowed = false; 215 216 /** @hide */ allowPrivilegedMediaPlaybackCapture()217 public boolean allowPrivilegedMediaPlaybackCapture() { 218 return mAllowPrivilegedPlaybackCapture; 219 } 220 221 /** @hide */ voiceCommunicationCaptureAllowed()222 public boolean voiceCommunicationCaptureAllowed() { 223 return mVoiceCommunicationCaptureAllowed; 224 } 225 226 /** @hide */ setVoiceCommunicationCaptureAllowed(boolean allowed)227 public void setVoiceCommunicationCaptureAllowed(boolean allowed) { 228 mVoiceCommunicationCaptureAllowed = allowed; 229 } 230 231 /** @hide */ 232 @Override equals(Object o)233 public boolean equals(Object o) { 234 if (this == o) return true; 235 if (o == null || getClass() != o.getClass()) return false; 236 237 final AudioMixingRule that = (AudioMixingRule) o; 238 return (this.mTargetMixType == that.mTargetMixType) 239 && (areCriteriaEquivalent(this.mCriteria, that.mCriteria) 240 && this.mAllowPrivilegedPlaybackCapture == that.mAllowPrivilegedPlaybackCapture 241 && this.mVoiceCommunicationCaptureAllowed 242 == that.mVoiceCommunicationCaptureAllowed); 243 } 244 245 @Override hashCode()246 public int hashCode() { 247 return Objects.hash( 248 mTargetMixType, 249 mCriteria, 250 mAllowPrivilegedPlaybackCapture, 251 mVoiceCommunicationCaptureAllowed); 252 } 253 isValidSystemApiRule(int rule)254 private static boolean isValidSystemApiRule(int rule) { 255 // API rules only expose the RULE_MATCH_* rules 256 switch (rule) { 257 case RULE_MATCH_ATTRIBUTE_USAGE: 258 case RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET: 259 case RULE_MATCH_UID: 260 case RULE_MATCH_USERID: 261 return true; 262 default: 263 return false; 264 } 265 } isValidAttributesSystemApiRule(int rule)266 private static boolean isValidAttributesSystemApiRule(int rule) { 267 // API rules only expose the RULE_MATCH_* rules 268 switch (rule) { 269 case RULE_MATCH_ATTRIBUTE_USAGE: 270 case RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET: 271 return true; 272 default: 273 return false; 274 } 275 } 276 isValidRule(int rule)277 private static boolean isValidRule(int rule) { 278 final int match_rule = rule & ~RULE_EXCLUSION_MASK; 279 switch (match_rule) { 280 case RULE_MATCH_ATTRIBUTE_USAGE: 281 case RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET: 282 case RULE_MATCH_UID: 283 case RULE_MATCH_USERID: 284 return true; 285 default: 286 return false; 287 } 288 } 289 isPlayerRule(int rule)290 private static boolean isPlayerRule(int rule) { 291 final int match_rule = rule & ~RULE_EXCLUSION_MASK; 292 switch (match_rule) { 293 case RULE_MATCH_ATTRIBUTE_USAGE: 294 case RULE_MATCH_USERID: 295 return true; 296 default: 297 return false; 298 } 299 } 300 isRecorderRule(int rule)301 private static boolean isRecorderRule(int rule) { 302 final int match_rule = rule & ~RULE_EXCLUSION_MASK; 303 switch (match_rule) { 304 case RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET: 305 return true; 306 default: 307 return false; 308 } 309 } 310 isAudioAttributeRule(int match_rule)311 private static boolean isAudioAttributeRule(int match_rule) { 312 switch(match_rule) { 313 case RULE_MATCH_ATTRIBUTE_USAGE: 314 case RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET: 315 return true; 316 default: 317 return false; 318 } 319 } 320 321 /** 322 * Builder class for {@link AudioMixingRule} objects 323 */ 324 public static class Builder { 325 private ArrayList<AudioMixMatchCriterion> mCriteria; 326 private int mTargetMixType = AudioMix.MIX_TYPE_INVALID; 327 private boolean mAllowPrivilegedMediaPlaybackCapture = false; 328 // This value should be set internally according to a permission check 329 private boolean mVoiceCommunicationCaptureAllowed = false; 330 331 /** 332 * Constructs a new Builder with no rules. 333 */ Builder()334 public Builder() { 335 mCriteria = new ArrayList<AudioMixMatchCriterion>(); 336 } 337 338 /** 339 * Add a rule for the selection of which streams are mixed together. 340 * @param attrToMatch a non-null AudioAttributes instance for which a contradictory 341 * rule hasn't been set yet. 342 * @param rule {@link AudioMixingRule#RULE_MATCH_ATTRIBUTE_USAGE} or 343 * {@link AudioMixingRule#RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET}. 344 * @return the same Builder instance. 345 * @throws IllegalArgumentException 346 * @see #excludeRule(AudioAttributes, int) 347 */ addRule(AudioAttributes attrToMatch, int rule)348 public Builder addRule(AudioAttributes attrToMatch, int rule) 349 throws IllegalArgumentException { 350 if (!isValidAttributesSystemApiRule(rule)) { 351 throw new IllegalArgumentException("Illegal rule value " + rule); 352 } 353 return checkAddRuleObjInternal(rule, attrToMatch); 354 } 355 356 /** 357 * Add a rule by exclusion for the selection of which streams are mixed together. 358 * <br>For instance the following code 359 * <br><pre> 360 * AudioAttributes mediaAttr = new AudioAttributes.Builder() 361 * .setUsage(AudioAttributes.USAGE_MEDIA) 362 * .build(); 363 * AudioMixingRule noMediaRule = new AudioMixingRule.Builder() 364 * .excludeRule(mediaAttr, AudioMixingRule.RULE_MATCH_ATTRIBUTE_USAGE) 365 * .build(); 366 * </pre> 367 * <br>will create a rule which maps to any usage value, except USAGE_MEDIA. 368 * @param attrToMatch a non-null AudioAttributes instance for which a contradictory 369 * rule hasn't been set yet. 370 * @param rule {@link AudioMixingRule#RULE_MATCH_ATTRIBUTE_USAGE} or 371 * {@link AudioMixingRule#RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET}. 372 * @return the same Builder instance. 373 * @throws IllegalArgumentException 374 * @see #addRule(AudioAttributes, int) 375 */ excludeRule(AudioAttributes attrToMatch, int rule)376 public Builder excludeRule(AudioAttributes attrToMatch, int rule) 377 throws IllegalArgumentException { 378 if (!isValidAttributesSystemApiRule(rule)) { 379 throw new IllegalArgumentException("Illegal rule value " + rule); 380 } 381 return checkAddRuleObjInternal(rule | RULE_EXCLUSION_MASK, attrToMatch); 382 } 383 384 /** 385 * Add a rule for the selection of which streams are mixed together. 386 * The rule defines what the matching will be made on. It also determines the type of the 387 * property to match against. 388 * @param rule one of {@link AudioMixingRule#RULE_MATCH_ATTRIBUTE_USAGE}, 389 * {@link AudioMixingRule#RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET} or 390 * {@link AudioMixingRule#RULE_MATCH_UID} or 391 * {@link AudioMixingRule#RULE_MATCH_USERID}. 392 * @param property see the definition of each rule for the type to use (either an 393 * {@link AudioAttributes} or an {@link java.lang.Integer}). 394 * @return the same Builder instance. 395 * @throws IllegalArgumentException 396 * @see #excludeMixRule(int, Object) 397 */ addMixRule(int rule, Object property)398 public Builder addMixRule(int rule, Object property) throws IllegalArgumentException { 399 if (!isValidSystemApiRule(rule)) { 400 throw new IllegalArgumentException("Illegal rule value " + rule); 401 } 402 return checkAddRuleObjInternal(rule, property); 403 } 404 405 /** 406 * Add a rule by exclusion for the selection of which streams are mixed together. 407 * <br>For instance the following code 408 * <br><pre> 409 * AudioAttributes mediaAttr = new AudioAttributes.Builder() 410 * .setUsage(AudioAttributes.USAGE_MEDIA) 411 * .build(); 412 * AudioMixingRule noMediaRule = new AudioMixingRule.Builder() 413 * .addMixRule(AudioMixingRule.RULE_MATCH_ATTRIBUTE_USAGE, mediaAttr) 414 * .excludeMixRule(AudioMixingRule.RULE_MATCH_UID, new Integer(uidToExclude) 415 * .build(); 416 * </pre> 417 * <br>will create a rule which maps to usage USAGE_MEDIA, but excludes any stream 418 * coming from the specified UID. 419 * @param rule one of {@link AudioMixingRule#RULE_MATCH_ATTRIBUTE_USAGE}, 420 * {@link AudioMixingRule#RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET} or 421 * {@link AudioMixingRule#RULE_MATCH_UID} or 422 * {@link AudioMixingRule#RULE_MATCH_USERID}. 423 * @param property see the definition of each rule for the type to use (either an 424 * {@link AudioAttributes} or an {@link java.lang.Integer}). 425 * @return the same Builder instance. 426 * @throws IllegalArgumentException 427 */ excludeMixRule(int rule, Object property)428 public Builder excludeMixRule(int rule, Object property) throws IllegalArgumentException { 429 if (!isValidSystemApiRule(rule)) { 430 throw new IllegalArgumentException("Illegal rule value " + rule); 431 } 432 return checkAddRuleObjInternal(rule | RULE_EXCLUSION_MASK, property); 433 } 434 435 /** 436 * Set if the audio of app that opted out of audio playback capture should be captured. 437 * 438 * Caller of this method with <code>true</code>, MUST abide to the restriction listed in 439 * {@link ALLOW_CAPTURE_BY_SYSTEM}, including but not limited to the captured audio 440 * can not leave the capturing app, and the quality is limited to 16k mono. 441 * 442 * The permission {@link CAPTURE_AUDIO_OUTPUT} or {@link CAPTURE_MEDIA_OUTPUT} is needed 443 * to ignore the opt-out. 444 * 445 * Only affects LOOPBACK|RENDER mix. 446 * 447 * @return the same Builder instance. 448 */ allowPrivilegedPlaybackCapture(boolean allow)449 public @NonNull Builder allowPrivilegedPlaybackCapture(boolean allow) { 450 mAllowPrivilegedMediaPlaybackCapture = allow; 451 return this; 452 } 453 454 /** 455 * Set if the caller of the rule is able to capture voice communication output. 456 * A system app can capture voice communication output only if it is granted with the. 457 * CAPTURE_VOICE_COMMUNICATION_OUTPUT permission. 458 * 459 * Note that this method is for internal use only and should not be called by the app that 460 * creates the rule. 461 * 462 * @return the same Builder instance. 463 * 464 * @hide 465 */ voiceCommunicationCaptureAllowed(boolean allowed)466 public @NonNull Builder voiceCommunicationCaptureAllowed(boolean allowed) { 467 mVoiceCommunicationCaptureAllowed = allowed; 468 return this; 469 } 470 471 /** 472 * Set target mix type of the mixing rule. 473 * 474 * <p>Note: If the mix type was not specified, it will be decided automatically by mixing 475 * rule. For {@link #RULE_MATCH_UID}, the default type is {@link AudioMix#MIX_TYPE_PLAYERS}. 476 * 477 * @param mixType {@link AudioMix#MIX_TYPE_PLAYERS} or {@link AudioMix#MIX_TYPE_RECORDERS} 478 * @return the same Builder instance. 479 * 480 * @hide 481 */ setTargetMixType(int mixType)482 public @NonNull Builder setTargetMixType(int mixType) { 483 mTargetMixType = mixType; 484 Log.i("AudioMixingRule", "Builder setTargetMixType " + mixType); 485 return this; 486 } 487 488 /** 489 * Add or exclude a rule for the selection of which streams are mixed together. 490 * Does error checking on the parameters. 491 * @param rule 492 * @param property 493 * @return the same Builder instance. 494 * @throws IllegalArgumentException 495 */ checkAddRuleObjInternal(int rule, Object property)496 private Builder checkAddRuleObjInternal(int rule, Object property) 497 throws IllegalArgumentException { 498 if (property == null) { 499 throw new IllegalArgumentException("Illegal null argument for mixing rule"); 500 } 501 if (!isValidRule(rule)) { 502 throw new IllegalArgumentException("Illegal rule value " + rule); 503 } 504 final int match_rule = rule & ~RULE_EXCLUSION_MASK; 505 if (isAudioAttributeRule(match_rule)) { 506 if (!(property instanceof AudioAttributes)) { 507 throw new IllegalArgumentException("Invalid AudioAttributes argument"); 508 } 509 return addRuleInternal((AudioAttributes) property, null, rule); 510 } else { 511 // implies integer match rule 512 if (!(property instanceof Integer)) { 513 throw new IllegalArgumentException("Invalid Integer argument"); 514 } 515 return addRuleInternal(null, (Integer) property, rule); 516 } 517 } 518 519 /** 520 * Add or exclude a rule on AudioAttributes or integer property for the selection of which 521 * streams are mixed together. 522 * No rule-to-parameter type check, all done in {@link #checkAddRuleObjInternal(int, Object)}. 523 * Exceptions are thrown only when incompatible rules are added. 524 * @param attrToMatch a non-null AudioAttributes instance for which a contradictory 525 * rule hasn't been set yet, null if not used. 526 * @param intProp an integer property to match or exclude, null if not used. 527 * @param rule one of {@link AudioMixingRule#RULE_EXCLUDE_ATTRIBUTE_USAGE}, 528 * {@link AudioMixingRule#RULE_MATCH_ATTRIBUTE_USAGE}, 529 * {@link AudioMixingRule#RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET} or 530 * {@link AudioMixingRule#RULE_EXCLUDE_ATTRIBUTE_CAPTURE_PRESET}, 531 * {@link AudioMixingRule#RULE_MATCH_UID}, {@link AudioMixingRule#RULE_EXCLUDE_UID}. 532 * {@link AudioMixingRule#RULE_MATCH_USERID}, 533 * {@link AudioMixingRule#RULE_EXCLUDE_USERID}. 534 * @return the same Builder instance. 535 * @throws IllegalArgumentException 536 */ addRuleInternal(AudioAttributes attrToMatch, Integer intProp, int rule)537 private Builder addRuleInternal(AudioAttributes attrToMatch, Integer intProp, int rule) 538 throws IllegalArgumentException { 539 // as rules are added to the Builder, we verify they are consistent with the type 540 // of mix being built. When adding the first rule, the mix type is MIX_TYPE_INVALID. 541 if (mTargetMixType == AudioMix.MIX_TYPE_INVALID) { 542 if (isPlayerRule(rule)) { 543 mTargetMixType = AudioMix.MIX_TYPE_PLAYERS; 544 } else if (isRecorderRule(rule)) { 545 mTargetMixType = AudioMix.MIX_TYPE_RECORDERS; 546 } else { 547 // For rules which are not player or recorder specific (e.g. RULE_MATCH_UID), 548 // the default mix type is MIX_TYPE_PLAYERS. 549 mTargetMixType = AudioMix.MIX_TYPE_PLAYERS; 550 } 551 } else if ((isPlayerRule(rule) && (mTargetMixType != AudioMix.MIX_TYPE_PLAYERS)) 552 || (isRecorderRule(rule)) && (mTargetMixType != AudioMix.MIX_TYPE_RECORDERS)) 553 { 554 throw new IllegalArgumentException("Incompatible rule for mix"); 555 } 556 synchronized (mCriteria) { 557 Iterator<AudioMixMatchCriterion> crIterator = mCriteria.iterator(); 558 final int match_rule = rule & ~RULE_EXCLUSION_MASK; 559 while (crIterator.hasNext()) { 560 final AudioMixMatchCriterion criterion = crIterator.next(); 561 562 if ((criterion.mRule & ~RULE_EXCLUSION_MASK) != match_rule) { 563 continue; // The two rules are not of the same type 564 } 565 switch (match_rule) { 566 case RULE_MATCH_ATTRIBUTE_USAGE: 567 // "usage"-based rule 568 if (criterion.mAttr.getSystemUsage() == attrToMatch.getSystemUsage()) { 569 if (criterion.mRule == rule) { 570 // rule already exists, we're done 571 return this; 572 } else { 573 // criterion already exists with a another rule, 574 // it is incompatible 575 throw new IllegalArgumentException("Contradictory rule exists" 576 + " for " + attrToMatch); 577 } 578 } 579 break; 580 case RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET: 581 // "capture preset"-base rule 582 if (criterion.mAttr.getCapturePreset() == attrToMatch.getCapturePreset()) { 583 if (criterion.mRule == rule) { 584 // rule already exists, we're done 585 return this; 586 } else { 587 // criterion already exists with a another rule, 588 // it is incompatible 589 throw new IllegalArgumentException("Contradictory rule exists" 590 + " for " + attrToMatch); 591 } 592 } 593 break; 594 case RULE_MATCH_UID: 595 // "usage"-based rule 596 if (criterion.mIntProp == intProp.intValue()) { 597 if (criterion.mRule == rule) { 598 // rule already exists, we're done 599 return this; 600 } else { 601 // criterion already exists with a another rule, 602 // it is incompatible 603 throw new IllegalArgumentException("Contradictory rule exists" 604 + " for UID " + intProp); 605 } 606 } 607 break; 608 case RULE_MATCH_USERID: 609 // "userid"-based rule 610 if (criterion.mIntProp == intProp.intValue()) { 611 if (criterion.mRule == rule) { 612 // rule already exists, we're done 613 return this; 614 } else { 615 // criterion already exists with a another rule, 616 // it is incompatible 617 throw new IllegalArgumentException("Contradictory rule exists" 618 + " for userId " + intProp); 619 } 620 } 621 break; 622 } 623 } 624 // rule didn't exist, add it 625 switch (match_rule) { 626 case RULE_MATCH_ATTRIBUTE_USAGE: 627 case RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET: 628 mCriteria.add(new AudioMixMatchCriterion(attrToMatch, rule)); 629 break; 630 case RULE_MATCH_UID: 631 case RULE_MATCH_USERID: 632 mCriteria.add(new AudioMixMatchCriterion(intProp, rule)); 633 break; 634 default: 635 throw new IllegalStateException("Unreachable code in addRuleInternal()"); 636 } 637 } 638 return this; 639 } 640 addRuleFromParcel(Parcel in)641 Builder addRuleFromParcel(Parcel in) throws IllegalArgumentException { 642 final int rule = in.readInt(); 643 final int match_rule = rule & ~RULE_EXCLUSION_MASK; 644 AudioAttributes attr = null; 645 Integer intProp = null; 646 switch (match_rule) { 647 case RULE_MATCH_ATTRIBUTE_USAGE: 648 int usage = in.readInt(); 649 if (AudioAttributes.isSystemUsage(usage)) { 650 attr = new AudioAttributes.Builder() 651 .setSystemUsage(usage).build(); 652 } else { 653 attr = new AudioAttributes.Builder() 654 .setUsage(usage).build(); 655 } 656 break; 657 case RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET: 658 int preset = in.readInt(); 659 attr = new AudioAttributes.Builder() 660 .setInternalCapturePreset(preset).build(); 661 break; 662 case RULE_MATCH_UID: 663 case RULE_MATCH_USERID: 664 intProp = new Integer(in.readInt()); 665 break; 666 default: 667 // assume there was in int value to read as for now they come in pair 668 in.readInt(); 669 throw new IllegalArgumentException("Illegal rule value " + rule + " in parcel"); 670 } 671 return addRuleInternal(attr, intProp, rule); 672 } 673 674 /** 675 * Combines all of the matching and exclusion rules that have been set and return a new 676 * {@link AudioMixingRule} object. 677 * @return a new {@link AudioMixingRule} object 678 */ build()679 public AudioMixingRule build() { 680 return new AudioMixingRule(mTargetMixType, mCriteria, 681 mAllowPrivilegedMediaPlaybackCapture, mVoiceCommunicationCaptureAllowed); 682 } 683 } 684 } 685