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 */ 165 @Override hashCode()166 public int hashCode() { 167 return Objects.hash(mRouteFlags, mRule, mMixType, mFormat); 168 } 169 170 /** @hide */ 171 @IntDef(flag = true, 172 value = { ROUTE_FLAG_RENDER, ROUTE_FLAG_LOOP_BACK } ) 173 @Retention(RetentionPolicy.SOURCE) 174 public @interface RouteFlags {} 175 176 /** 177 * Builder class for {@link AudioMix} objects 178 * 179 */ 180 @SystemApi 181 public static class Builder { 182 private AudioMixingRule mRule = null; 183 private AudioFormat mFormat = null; 184 private int mRouteFlags = 0; 185 private int mCallbackFlags = 0; 186 // an AudioSystem.DEVICE_* value, not AudioDeviceInfo.TYPE_* 187 private int mDeviceSystemType = AudioSystem.DEVICE_NONE; 188 private String mDeviceAddress = null; 189 190 /** 191 * @hide 192 * Only used by AudioPolicyConfig, not a public API. 193 */ Builder()194 Builder() { } 195 196 /** 197 * Construct an instance for the given {@link AudioMixingRule}. 198 * @param rule a non-null {@link AudioMixingRule} instance. 199 * @throws IllegalArgumentException 200 */ 201 @SystemApi Builder(AudioMixingRule rule)202 public Builder(AudioMixingRule rule) 203 throws IllegalArgumentException { 204 if (rule == null) { 205 throw new IllegalArgumentException("Illegal null AudioMixingRule argument"); 206 } 207 mRule = rule; 208 } 209 210 /** 211 * @hide 212 * Only used by AudioPolicyConfig, not a public API. 213 * @param rule 214 * @return the same Builder instance. 215 * @throws IllegalArgumentException 216 */ setMixingRule(AudioMixingRule rule)217 Builder setMixingRule(AudioMixingRule rule) 218 throws IllegalArgumentException { 219 if (rule == null) { 220 throw new IllegalArgumentException("Illegal null AudioMixingRule argument"); 221 } 222 mRule = rule; 223 return this; 224 } 225 226 /** 227 * @hide 228 * Only used by AudioPolicyConfig, not a public API. 229 * @param callbackFlags which callbacks are called from native 230 * @return the same Builder instance. 231 * @throws IllegalArgumentException 232 */ setCallbackFlags(int flags)233 Builder setCallbackFlags(int flags) throws IllegalArgumentException { 234 if ((flags != 0) && ((flags & CALLBACK_FLAGS_ALL) == 0)) { 235 throw new IllegalArgumentException("Illegal callback flags 0x" 236 + Integer.toHexString(flags).toUpperCase()); 237 } 238 mCallbackFlags = flags; 239 return this; 240 } 241 242 /** 243 * @hide 244 * Only used by AudioPolicyConfig, not a public API. 245 * @param deviceType an AudioSystem.DEVICE_* value, not AudioDeviceInfo.TYPE_* 246 * @param address 247 * @return the same Builder instance. 248 */ setDevice(int deviceType, String address)249 Builder setDevice(int deviceType, String address) { 250 mDeviceSystemType = deviceType; 251 mDeviceAddress = address; 252 return this; 253 } 254 255 /** 256 * Sets the {@link AudioFormat} for the mix. 257 * @param format a non-null {@link AudioFormat} instance. 258 * @return the same Builder instance. 259 * @throws IllegalArgumentException 260 */ 261 @SystemApi setFormat(AudioFormat format)262 public Builder setFormat(AudioFormat format) 263 throws IllegalArgumentException { 264 if (format == null) { 265 throw new IllegalArgumentException("Illegal null AudioFormat argument"); 266 } 267 mFormat = format; 268 return this; 269 } 270 271 /** 272 * Sets the routing behavior for the mix. If not set, routing behavior will default to 273 * {@link AudioMix#ROUTE_FLAG_LOOP_BACK}. 274 * @param routeFlags one of {@link AudioMix#ROUTE_FLAG_LOOP_BACK}, 275 * {@link AudioMix#ROUTE_FLAG_RENDER} 276 * @return the same Builder instance. 277 * @throws IllegalArgumentException 278 */ 279 @SystemApi setRouteFlags(@outeFlags int routeFlags)280 public Builder setRouteFlags(@RouteFlags int routeFlags) 281 throws IllegalArgumentException { 282 if (routeFlags == 0) { 283 throw new IllegalArgumentException("Illegal empty route flags"); 284 } 285 if ((routeFlags & ROUTE_FLAG_SUPPORTED) == 0) { 286 throw new IllegalArgumentException("Invalid route flags 0x" 287 + Integer.toHexString(routeFlags) + "when configuring an AudioMix"); 288 } 289 if ((routeFlags & ~ROUTE_FLAG_SUPPORTED) != 0) { 290 throw new IllegalArgumentException("Unknown route flags 0x" 291 + Integer.toHexString(routeFlags) + "when configuring an AudioMix"); 292 } 293 mRouteFlags = routeFlags; 294 return this; 295 } 296 297 /** 298 * Sets the audio device used for playback. Cannot be used in the context of an audio 299 * policy used to inject audio to be recorded, or in a mix whose route flags doesn't 300 * specify {@link AudioMix#ROUTE_FLAG_RENDER}. 301 * @param device a non-null AudioDeviceInfo describing the audio device to play the output 302 * of this mix. 303 * @return the same Builder instance 304 * @throws IllegalArgumentException 305 */ 306 @SystemApi setDevice(@onNull AudioDeviceInfo device)307 public Builder setDevice(@NonNull AudioDeviceInfo device) throws IllegalArgumentException { 308 if (device == null) { 309 throw new IllegalArgumentException("Illegal null AudioDeviceInfo argument"); 310 } 311 if (!device.isSink()) { 312 throw new IllegalArgumentException("Unsupported device type on mix, not a sink"); 313 } 314 mDeviceSystemType = AudioDeviceInfo.convertDeviceTypeToInternalDevice(device.getType()); 315 mDeviceAddress = device.getAddress(); 316 return this; 317 } 318 319 /** 320 * Combines all of the settings and return a new {@link AudioMix} object. 321 * @return a new {@link AudioMix} object 322 * @throws IllegalArgumentException if no {@link AudioMixingRule} has been set. 323 */ 324 @SystemApi build()325 public AudioMix build() throws IllegalArgumentException { 326 if (mRule == null) { 327 throw new IllegalArgumentException("Illegal null AudioMixingRule"); 328 } 329 if (mRouteFlags == 0) { 330 // no route flags set, use default as described in Builder.setRouteFlags(int) 331 mRouteFlags = ROUTE_FLAG_LOOP_BACK; 332 } 333 // can't do loop back AND render at same time in this implementation 334 if (mRouteFlags == (ROUTE_FLAG_RENDER | ROUTE_FLAG_LOOP_BACK)) { 335 throw new IllegalArgumentException("Unsupported route behavior combination 0x" + 336 Integer.toHexString(mRouteFlags)); 337 } 338 if (mFormat == null) { 339 // FIXME Can we eliminate this? Will AudioMix work with an unspecified sample rate? 340 int rate = AudioSystem.getPrimaryOutputSamplingRate(); 341 if (rate <= 0) { 342 rate = 44100; 343 } 344 mFormat = new AudioFormat.Builder().setSampleRate(rate).build(); 345 } 346 if ((mDeviceSystemType != AudioSystem.DEVICE_NONE) 347 && (mDeviceSystemType != AudioSystem.DEVICE_OUT_REMOTE_SUBMIX) 348 && (mDeviceSystemType != AudioSystem.DEVICE_IN_REMOTE_SUBMIX)) { 349 if ((mRouteFlags & ROUTE_FLAG_RENDER) == 0) { 350 throw new IllegalArgumentException( 351 "Can't have audio device without flag ROUTE_FLAG_RENDER"); 352 } 353 if (mRule.getTargetMixType() != AudioMix.MIX_TYPE_PLAYERS) { 354 throw new IllegalArgumentException("Unsupported device on non-playback mix"); 355 } 356 } else { 357 if ((mRouteFlags & ROUTE_FLAG_RENDER) == ROUTE_FLAG_RENDER) { 358 throw new IllegalArgumentException( 359 "Can't have flag ROUTE_FLAG_RENDER without an audio device"); 360 } 361 if ((mRouteFlags & ROUTE_FLAG_SUPPORTED) == ROUTE_FLAG_LOOP_BACK) { 362 if (mRule.getTargetMixType() == MIX_TYPE_PLAYERS) { 363 mDeviceSystemType = AudioSystem.DEVICE_OUT_REMOTE_SUBMIX; 364 } else if (mRule.getTargetMixType() == MIX_TYPE_RECORDERS) { 365 mDeviceSystemType = AudioSystem.DEVICE_IN_REMOTE_SUBMIX; 366 } else { 367 throw new IllegalArgumentException("Unknown mixing rule type"); 368 } 369 } 370 } 371 return new AudioMix(mRule, mFormat, mRouteFlags, mCallbackFlags, mDeviceSystemType, 372 mDeviceAddress); 373 } 374 } 375 } 376