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