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