• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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