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