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