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.media.AudioFormat;
20 import android.media.audiopolicy.AudioMixingRule.AttributeMatchCriterion;
21 import android.os.Parcel;
22 import android.os.Parcelable;
23 import android.util.Log;
24 
25 import java.util.ArrayList;
26 import java.util.Objects;
27 
28 /**
29  * @hide
30  * Internal storage class for AudioPolicy configuration.
31  */
32 public class AudioPolicyConfig implements Parcelable {
33 
34     private static final String TAG = "AudioPolicyConfig";
35 
36     protected ArrayList<AudioMix> mMixes;
37     protected int mDuckingPolicy = AudioPolicy.FOCUS_POLICY_DUCKING_IN_APP;
38 
39     private String mRegistrationId = null;
40 
AudioPolicyConfig(AudioPolicyConfig conf)41     protected AudioPolicyConfig(AudioPolicyConfig conf) {
42         mMixes = conf.mMixes;
43     }
44 
AudioPolicyConfig(ArrayList<AudioMix> mixes)45     AudioPolicyConfig(ArrayList<AudioMix> mixes) {
46         mMixes = mixes;
47     }
48 
49     /**
50      * Add an {@link AudioMix} to be part of the audio policy being built.
51      * @param mix a non-null {@link AudioMix} to be part of the audio policy.
52      * @return the same Builder instance.
53      * @throws IllegalArgumentException
54      */
addMix(AudioMix mix)55     public void addMix(AudioMix mix) throws IllegalArgumentException {
56         if (mix == null) {
57             throw new IllegalArgumentException("Illegal null AudioMix argument");
58         }
59         mMixes.add(mix);
60     }
61 
getMixes()62     public ArrayList<AudioMix> getMixes() {
63         return mMixes;
64     }
65 
66     @Override
hashCode()67     public int hashCode() {
68         return Objects.hash(mMixes);
69     }
70 
71     @Override
describeContents()72     public int describeContents() {
73         return 0;
74     }
75 
76     @Override
writeToParcel(Parcel dest, int flags)77     public void writeToParcel(Parcel dest, int flags) {
78         dest.writeInt(mMixes.size());
79         for (AudioMix mix : mMixes) {
80             // write mix route flags
81             dest.writeInt(mix.getRouteFlags());
82             // write callback flags
83             dest.writeInt(mix.mCallbackFlags);
84             // write mix format
85             dest.writeInt(mix.getFormat().getSampleRate());
86             dest.writeInt(mix.getFormat().getEncoding());
87             dest.writeInt(mix.getFormat().getChannelMask());
88             // write mix rules
89             final ArrayList<AttributeMatchCriterion> criteria = mix.getRule().getCriteria();
90             dest.writeInt(criteria.size());
91             for (AttributeMatchCriterion criterion : criteria) {
92                 criterion.writeToParcel(dest);
93             }
94         }
95     }
96 
AudioPolicyConfig(Parcel in)97     private AudioPolicyConfig(Parcel in) {
98         mMixes = new ArrayList<AudioMix>();
99         int nbMixes = in.readInt();
100         for (int i = 0 ; i < nbMixes ; i++) {
101             final AudioMix.Builder mixBuilder = new AudioMix.Builder();
102             // read mix route flags
103             int routeFlags = in.readInt();
104             mixBuilder.setRouteFlags(routeFlags);
105             // read callback flags
106             mixBuilder.setCallbackFlags(in.readInt());
107             // read mix format
108             int sampleRate = in.readInt();
109             int encoding = in.readInt();
110             int channelMask = in.readInt();
111             final AudioFormat format = new AudioFormat.Builder().setSampleRate(sampleRate)
112                     .setChannelMask(channelMask).setEncoding(encoding).build();
113             mixBuilder.setFormat(format);
114             // read mix rules
115             int nbRules = in.readInt();
116             AudioMixingRule.Builder ruleBuilder = new AudioMixingRule.Builder();
117             for (int j = 0 ; j < nbRules ; j++) {
118                 // read the matching rules
119                 ruleBuilder.addRuleFromParcel(in);
120             }
121             mixBuilder.setMixingRule(ruleBuilder.build());
122             mMixes.add(mixBuilder.build());
123         }
124     }
125 
126     public static final Parcelable.Creator<AudioPolicyConfig> CREATOR
127             = new Parcelable.Creator<AudioPolicyConfig>() {
128         /**
129          * Rebuilds an AudioPolicyConfig previously stored with writeToParcel().
130          * @param p Parcel object to read the AudioPolicyConfig from
131          * @return a new AudioPolicyConfig created from the data in the parcel
132          */
133         public AudioPolicyConfig createFromParcel(Parcel p) {
134             return new AudioPolicyConfig(p);
135         }
136         public AudioPolicyConfig[] newArray(int size) {
137             return new AudioPolicyConfig[size];
138         }
139     };
140 
toLogFriendlyString()141     public String toLogFriendlyString () {
142         String textDump = new String("android.media.audiopolicy.AudioPolicyConfig:\n");
143         textDump += mMixes.size() + " AudioMix: "+ mRegistrationId + "\n";
144         for(AudioMix mix : mMixes) {
145             // write mix route flags
146             textDump += "* route flags=0x" + Integer.toHexString(mix.getRouteFlags()) + "\n";
147             // write mix format
148             textDump += "  rate=" + mix.getFormat().getSampleRate() + "Hz\n";
149             textDump += "  encoding=" + mix.getFormat().getEncoding() + "\n";
150             textDump += "  channels=0x";
151             textDump += Integer.toHexString(mix.getFormat().getChannelMask()).toUpperCase() +"\n";
152             // write mix rules
153             final ArrayList<AttributeMatchCriterion> criteria = mix.getRule().getCriteria();
154             for (AttributeMatchCriterion criterion : criteria) {
155                 switch(criterion.mRule) {
156                     case AudioMixingRule.RULE_EXCLUDE_ATTRIBUTE_USAGE:
157                         textDump += "  exclude usage ";
158                         textDump += criterion.mAttr.usageToString();
159                         break;
160                     case AudioMixingRule.RULE_MATCH_ATTRIBUTE_USAGE:
161                         textDump += "  match usage ";
162                         textDump += criterion.mAttr.usageToString();
163                         break;
164                     case AudioMixingRule.RULE_EXCLUDE_ATTRIBUTE_CAPTURE_PRESET:
165                         textDump += "  exclude capture preset ";
166                         textDump += criterion.mAttr.getCapturePreset();
167                         break;
168                     case AudioMixingRule.RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET:
169                         textDump += "  match capture preset ";
170                         textDump += criterion.mAttr.getCapturePreset();
171                         break;
172                     default:
173                         textDump += "invalid rule!";
174                 }
175                 textDump += "\n";
176             }
177         }
178         return textDump;
179     }
180 
setRegistration(String regId)181     protected void setRegistration(String regId) {
182         final boolean currentRegNull = (mRegistrationId == null) || mRegistrationId.isEmpty();
183         final boolean newRegNull = (regId == null) || regId.isEmpty();
184         if (!currentRegNull && !newRegNull && !mRegistrationId.equals(regId)) {
185             Log.e(TAG, "Invalid registration transition from " + mRegistrationId + " to " + regId);
186             return;
187         }
188         mRegistrationId = regId == null ? "" : regId;
189         int mixIndex = 0;
190         for (AudioMix mix : mMixes) {
191             if (!mRegistrationId.isEmpty()) {
192                 mix.setRegistration(mRegistrationId + "mix" + mixTypeId(mix.getMixType()) + ":"
193                         + mixIndex++);
194             } else {
195                 mix.setRegistration("");
196             }
197         }
198     }
199 
mixTypeId(int type)200     private static String mixTypeId(int type) {
201         if (type == AudioMix.MIX_TYPE_PLAYERS) return "p";
202         else if (type == AudioMix.MIX_TYPE_RECORDERS) return "r";
203         else return "i";
204     }
205 
getRegistration()206     protected String getRegistration() {
207         return mRegistrationId;
208     }
209 }
210