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.IntDef; 20 import android.annotation.NonNull; 21 import android.annotation.SystemApi; 22 import android.media.AudioDeviceInfo; 23 import android.media.AudioFormat; 24 import android.media.AudioSystem; 25 26 import java.lang.annotation.Retention; 27 import java.lang.annotation.RetentionPolicy; 28 import java.util.Objects; 29 30 /** 31 * @hide 32 */ 33 @SystemApi 34 public class AudioMix { 35 36 private AudioMixingRule mRule; 37 private AudioFormat mFormat; 38 private int mRouteFlags; 39 private int mMixType = MIX_TYPE_INVALID; 40 41 // written by AudioPolicy 42 int mMixState = MIX_STATE_DISABLED; 43 int mCallbackFlags; 44 String mDeviceAddress; 45 46 // initialized in constructor, read by AudioPolicyConfig 47 final int mDeviceSystemType; // an AudioSystem.DEVICE_* value, not AudioDeviceInfo.TYPE_* 48 49 /** 50 * All parameters are guaranteed valid through the Builder. 51 */ AudioMix(AudioMixingRule rule, AudioFormat format, int routeFlags, int callbackFlags, int deviceType, String deviceAddress)52 private AudioMix(AudioMixingRule rule, AudioFormat format, int routeFlags, int callbackFlags, 53 int deviceType, String deviceAddress) { 54 mRule = rule; 55 mFormat = format; 56 mRouteFlags = routeFlags; 57 mMixType = rule.getTargetMixType(); 58 mCallbackFlags = callbackFlags; 59 mDeviceSystemType = deviceType; 60 mDeviceAddress = (deviceAddress == null) ? new String("") : deviceAddress; 61 } 62 63 // CALLBACK_FLAG_* values: keep in sync with AudioMix::kCbFlag* values defined 64 // in frameworks/av/include/media/AudioPolicy.h 65 /** @hide */ 66 public final static int CALLBACK_FLAG_NOTIFY_ACTIVITY = 0x1; 67 // when adding new MIX_FLAG_* flags, add them to this mask of authorized masks: 68 private final static int CALLBACK_FLAGS_ALL = CALLBACK_FLAG_NOTIFY_ACTIVITY; 69 70 // ROUTE_FLAG_* values: keep in sync with MIX_ROUTE_FLAG_* values defined 71 // in frameworks/av/include/media/AudioPolicy.h 72 /** 73 * An audio mix behavior where the output of the mix is sent to the original destination of 74 * the audio signal, i.e. an output device for an output mix, or a recording for an input mix. 75 */ 76 @SystemApi 77 public static final int ROUTE_FLAG_RENDER = 0x1; 78 /** 79 * An audio mix behavior where the output of the mix is rerouted back to the framework and 80 * is accessible for injection or capture through the {@link AudioTrack} and {@link AudioRecord} 81 * APIs. 82 */ 83 @SystemApi 84 public static final int ROUTE_FLAG_LOOP_BACK = 0x1 << 1; 85 86 private static final int ROUTE_FLAG_SUPPORTED = ROUTE_FLAG_RENDER | ROUTE_FLAG_LOOP_BACK; 87 88 // MIX_TYPE_* values to keep in sync with frameworks/av/include/media/AudioPolicy.h 89 /** 90 * @hide 91 * Invalid mix type, default value. 92 */ 93 public static final int MIX_TYPE_INVALID = -1; 94 /** 95 * @hide 96 * Mix type indicating playback streams are mixed. 97 */ 98 public static final int MIX_TYPE_PLAYERS = 0; 99 /** 100 * @hide 101 * Mix type indicating recording streams are mixed. 102 */ 103 public static final int MIX_TYPE_RECORDERS = 1; 104 105 106 // MIX_STATE_* values to keep in sync with frameworks/av/include/media/AudioPolicy.h 107 /** 108 * @hide 109 * State of a mix before its policy is enabled. 110 */ 111 @SystemApi 112 public static final int MIX_STATE_DISABLED = -1; 113 /** 114 * @hide 115 * State of a mix when there is no audio to mix. 116 */ 117 @SystemApi 118 public static final int MIX_STATE_IDLE = 0; 119 /** 120 * @hide 121 * State of a mix that is actively mixing audio. 122 */ 123 @SystemApi 124 public static final int MIX_STATE_MIXING = 1; 125 126 /** 127 * @hide 128 * The current mixing state. 129 * @return one of {@link #MIX_STATE_DISABLED}, {@link #MIX_STATE_IDLE}, 130 * {@link #MIX_STATE_MIXING}. 131 */ 132 @SystemApi getMixState()133 public int getMixState() { 134 return mMixState; 135 } 136 137 getRouteFlags()138 int getRouteFlags() { 139 return mRouteFlags; 140 } 141 getFormat()142 AudioFormat getFormat() { 143 return mFormat; 144 } 145 getRule()146 AudioMixingRule getRule() { 147 return mRule; 148 } 149 150 /** @hide */ getMixType()151 public int getMixType() { 152 return mMixType; 153 } 154 setRegistration(String regId)155 void setRegistration(String regId) { 156 mDeviceAddress = regId; 157 } 158 159 /** @hide */ getRegistration()160 public String getRegistration() { 161 return mDeviceAddress; 162 } 163 164 /** @hide */ isAffectingUsage(int usage)165 public boolean isAffectingUsage(int usage) { 166 return mRule.isAffectingUsage(usage); 167 } 168 169 /** @hide */ 170 @Override equals(Object o)171 public boolean equals(Object o) { 172 if (this == o) return true; 173 if (o == null || getClass() != o.getClass()) return false; 174 175 final AudioMix that = (AudioMix) o; 176 return (this.mRouteFlags == that.mRouteFlags) 177 && (this.mRule == that.mRule) 178 && (this.mMixType == that.mMixType) 179 && (this.mFormat == that.mFormat); 180 } 181 182 /** @hide */ 183 @Override hashCode()184 public int hashCode() { 185 return Objects.hash(mRouteFlags, mRule, mMixType, mFormat); 186 } 187 188 /** @hide */ 189 @IntDef(flag = true, 190 value = { ROUTE_FLAG_RENDER, ROUTE_FLAG_LOOP_BACK } ) 191 @Retention(RetentionPolicy.SOURCE) 192 public @interface RouteFlags {} 193 194 /** 195 * Builder class for {@link AudioMix} objects 196 * 197 */ 198 @SystemApi 199 public static class Builder { 200 private AudioMixingRule mRule = null; 201 private AudioFormat mFormat = null; 202 private int mRouteFlags = 0; 203 private int mCallbackFlags = 0; 204 // an AudioSystem.DEVICE_* value, not AudioDeviceInfo.TYPE_* 205 private int mDeviceSystemType = AudioSystem.DEVICE_NONE; 206 private String mDeviceAddress = null; 207 208 /** 209 * @hide 210 * Only used by AudioPolicyConfig, not a public API. 211 */ Builder()212 Builder() { } 213 214 /** 215 * Construct an instance for the given {@link AudioMixingRule}. 216 * @param rule a non-null {@link AudioMixingRule} instance. 217 * @throws IllegalArgumentException 218 */ 219 @SystemApi Builder(AudioMixingRule rule)220 public Builder(AudioMixingRule rule) 221 throws IllegalArgumentException { 222 if (rule == null) { 223 throw new IllegalArgumentException("Illegal null AudioMixingRule argument"); 224 } 225 mRule = rule; 226 } 227 228 /** 229 * @hide 230 * Only used by AudioPolicyConfig, not a public API. 231 * @param rule 232 * @return the same Builder instance. 233 * @throws IllegalArgumentException 234 */ setMixingRule(AudioMixingRule rule)235 Builder setMixingRule(AudioMixingRule rule) 236 throws IllegalArgumentException { 237 if (rule == null) { 238 throw new IllegalArgumentException("Illegal null AudioMixingRule argument"); 239 } 240 mRule = rule; 241 return this; 242 } 243 244 /** 245 * @hide 246 * Only used by AudioPolicyConfig, not a public API. 247 * @param callbackFlags which callbacks are called from native 248 * @return the same Builder instance. 249 * @throws IllegalArgumentException 250 */ setCallbackFlags(int flags)251 Builder setCallbackFlags(int flags) throws IllegalArgumentException { 252 if ((flags != 0) && ((flags & CALLBACK_FLAGS_ALL) == 0)) { 253 throw new IllegalArgumentException("Illegal callback flags 0x" 254 + Integer.toHexString(flags).toUpperCase()); 255 } 256 mCallbackFlags = flags; 257 return this; 258 } 259 260 /** 261 * @hide 262 * Only used by AudioPolicyConfig, not a public API. 263 * @param deviceType an AudioSystem.DEVICE_* value, not AudioDeviceInfo.TYPE_* 264 * @param address 265 * @return the same Builder instance. 266 */ setDevice(int deviceType, String address)267 Builder setDevice(int deviceType, String address) { 268 mDeviceSystemType = deviceType; 269 mDeviceAddress = address; 270 return this; 271 } 272 273 /** 274 * Sets the {@link AudioFormat} for the mix. 275 * @param format a non-null {@link AudioFormat} instance. 276 * @return the same Builder instance. 277 * @throws IllegalArgumentException 278 */ 279 @SystemApi setFormat(AudioFormat format)280 public Builder setFormat(AudioFormat format) 281 throws IllegalArgumentException { 282 if (format == null) { 283 throw new IllegalArgumentException("Illegal null AudioFormat argument"); 284 } 285 mFormat = format; 286 return this; 287 } 288 289 /** 290 * Sets the routing behavior for the mix. If not set, routing behavior will default to 291 * {@link AudioMix#ROUTE_FLAG_LOOP_BACK}. 292 * @param routeFlags one of {@link AudioMix#ROUTE_FLAG_LOOP_BACK}, 293 * {@link AudioMix#ROUTE_FLAG_RENDER} 294 * @return the same Builder instance. 295 * @throws IllegalArgumentException 296 */ 297 @SystemApi setRouteFlags(@outeFlags int routeFlags)298 public Builder setRouteFlags(@RouteFlags int routeFlags) 299 throws IllegalArgumentException { 300 if (routeFlags == 0) { 301 throw new IllegalArgumentException("Illegal empty route flags"); 302 } 303 if ((routeFlags & ROUTE_FLAG_SUPPORTED) == 0) { 304 throw new IllegalArgumentException("Invalid route flags 0x" 305 + Integer.toHexString(routeFlags) + "when configuring an AudioMix"); 306 } 307 if ((routeFlags & ~ROUTE_FLAG_SUPPORTED) != 0) { 308 throw new IllegalArgumentException("Unknown route flags 0x" 309 + Integer.toHexString(routeFlags) + "when configuring an AudioMix"); 310 } 311 mRouteFlags = routeFlags; 312 return this; 313 } 314 315 /** 316 * Sets the audio device used for playback. Cannot be used in the context of an audio 317 * policy used to inject audio to be recorded, or in a mix whose route flags doesn't 318 * specify {@link AudioMix#ROUTE_FLAG_RENDER}. 319 * @param device a non-null AudioDeviceInfo describing the audio device to play the output 320 * of this mix. 321 * @return the same Builder instance 322 * @throws IllegalArgumentException 323 */ 324 @SystemApi setDevice(@onNull AudioDeviceInfo device)325 public Builder setDevice(@NonNull AudioDeviceInfo device) throws IllegalArgumentException { 326 if (device == null) { 327 throw new IllegalArgumentException("Illegal null AudioDeviceInfo argument"); 328 } 329 if (!device.isSink()) { 330 throw new IllegalArgumentException("Unsupported device type on mix, not a sink"); 331 } 332 mDeviceSystemType = AudioDeviceInfo.convertDeviceTypeToInternalDevice(device.getType()); 333 mDeviceAddress = device.getAddress(); 334 return this; 335 } 336 337 /** 338 * Combines all of the settings and return a new {@link AudioMix} object. 339 * @return a new {@link AudioMix} object 340 * @throws IllegalArgumentException if no {@link AudioMixingRule} has been set. 341 */ 342 @SystemApi build()343 public AudioMix build() throws IllegalArgumentException { 344 if (mRule == null) { 345 throw new IllegalArgumentException("Illegal null AudioMixingRule"); 346 } 347 if (mRouteFlags == 0) { 348 // no route flags set, use default as described in Builder.setRouteFlags(int) 349 mRouteFlags = ROUTE_FLAG_LOOP_BACK; 350 } 351 // can't do loop back AND render at same time in this implementation 352 if (mRouteFlags == (ROUTE_FLAG_RENDER | ROUTE_FLAG_LOOP_BACK)) { 353 throw new IllegalArgumentException("Unsupported route behavior combination 0x" + 354 Integer.toHexString(mRouteFlags)); 355 } 356 if (mFormat == null) { 357 // FIXME Can we eliminate this? Will AudioMix work with an unspecified sample rate? 358 int rate = AudioSystem.getPrimaryOutputSamplingRate(); 359 if (rate <= 0) { 360 rate = 44100; 361 } 362 mFormat = new AudioFormat.Builder().setSampleRate(rate).build(); 363 } 364 if ((mDeviceSystemType != AudioSystem.DEVICE_NONE) 365 && (mDeviceSystemType != AudioSystem.DEVICE_OUT_REMOTE_SUBMIX) 366 && (mDeviceSystemType != AudioSystem.DEVICE_IN_REMOTE_SUBMIX)) { 367 if ((mRouteFlags & ROUTE_FLAG_RENDER) == 0) { 368 throw new IllegalArgumentException( 369 "Can't have audio device without flag ROUTE_FLAG_RENDER"); 370 } 371 if (mRule.getTargetMixType() != AudioMix.MIX_TYPE_PLAYERS) { 372 throw new IllegalArgumentException("Unsupported device on non-playback mix"); 373 } 374 } else { 375 if ((mRouteFlags & ROUTE_FLAG_RENDER) == ROUTE_FLAG_RENDER) { 376 throw new IllegalArgumentException( 377 "Can't have flag ROUTE_FLAG_RENDER without an audio device"); 378 } 379 if ((mRouteFlags & ROUTE_FLAG_SUPPORTED) == ROUTE_FLAG_LOOP_BACK) { 380 if (mRule.getTargetMixType() == MIX_TYPE_PLAYERS) { 381 mDeviceSystemType = AudioSystem.DEVICE_OUT_REMOTE_SUBMIX; 382 } else if (mRule.getTargetMixType() == MIX_TYPE_RECORDERS) { 383 mDeviceSystemType = AudioSystem.DEVICE_IN_REMOTE_SUBMIX; 384 } else { 385 throw new IllegalArgumentException("Unknown mixing rule type"); 386 } 387 } 388 } 389 return new AudioMix(mRule, mFormat, mRouteFlags, mCallbackFlags, mDeviceSystemType, 390 mDeviceAddress); 391 } 392 } 393 } 394