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