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