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.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 /** @hide */ isRoutedToDevice(int deviceType, @NonNull String deviceAddress)196 public boolean isRoutedToDevice(int deviceType, @NonNull String deviceAddress) { 197 if ((mRouteFlags & ROUTE_FLAG_RENDER) != ROUTE_FLAG_RENDER) { 198 return false; 199 } 200 if (deviceType != mDeviceSystemType) { 201 return false; 202 } 203 if (!deviceAddress.equals(mDeviceAddress)) { 204 return false; 205 } 206 return true; 207 } 208 209 /** @return an error string if the format would not allow Privileged playbackCapture 210 * null otherwise 211 * @hide */ canBeUsedForPrivilegedCapture(AudioFormat format)212 public static String canBeUsedForPrivilegedCapture(AudioFormat format) { 213 int sampleRate = format.getSampleRate(); 214 if (sampleRate > PRIVILEDGED_CAPTURE_MAX_SAMPLE_RATE || sampleRate <= 0) { 215 return "Privileged audio capture sample rate " + sampleRate 216 + " can not be over " + PRIVILEDGED_CAPTURE_MAX_SAMPLE_RATE + "kHz"; 217 } 218 int channelCount = format.getChannelCount(); 219 if (channelCount > PRIVILEDGED_CAPTURE_MAX_CHANNEL_NUMBER || channelCount <= 0) { 220 return "Privileged audio capture channel count " + channelCount + " can not be over " 221 + PRIVILEDGED_CAPTURE_MAX_CHANNEL_NUMBER; 222 } 223 int encoding = format.getEncoding(); 224 if (!format.isPublicEncoding(encoding) || !format.isEncodingLinearPcm(encoding)) { 225 return "Privileged audio capture encoding " + encoding + "is not linear"; 226 } 227 if (format.getBytesPerSample(encoding) > PRIVILEDGED_CAPTURE_MAX_BYTES_PER_SAMPLE) { 228 return "Privileged audio capture encoding " + encoding + " can not be over " 229 + PRIVILEDGED_CAPTURE_MAX_BYTES_PER_SAMPLE + " bytes per sample"; 230 } 231 return null; 232 } 233 234 /** @hide */ 235 @Override equals(Object o)236 public boolean equals(Object o) { 237 if (this == o) return true; 238 if (o == null || getClass() != o.getClass()) return false; 239 240 final AudioMix that = (AudioMix) o; 241 return (this.mRouteFlags == that.mRouteFlags) 242 && (this.mRule == that.mRule) 243 && (this.mMixType == that.mMixType) 244 && (this.mFormat == that.mFormat); 245 } 246 247 /** @hide */ 248 @Override hashCode()249 public int hashCode() { 250 return Objects.hash(mRouteFlags, mRule, mMixType, mFormat); 251 } 252 253 /** @hide */ 254 @IntDef(flag = true, 255 value = { ROUTE_FLAG_RENDER, ROUTE_FLAG_LOOP_BACK } ) 256 @Retention(RetentionPolicy.SOURCE) 257 public @interface RouteFlags {} 258 259 /** 260 * Builder class for {@link AudioMix} objects 261 */ 262 public static class Builder { 263 private AudioMixingRule mRule = null; 264 private AudioFormat mFormat = null; 265 private int mRouteFlags = 0; 266 private int mCallbackFlags = 0; 267 // an AudioSystem.DEVICE_* value, not AudioDeviceInfo.TYPE_* 268 private int mDeviceSystemType = AudioSystem.DEVICE_NONE; 269 private String mDeviceAddress = null; 270 271 /** 272 * @hide 273 * Only used by AudioPolicyConfig, not a public API. 274 */ Builder()275 Builder() { } 276 277 /** 278 * Construct an instance for the given {@link AudioMixingRule}. 279 * @param rule a non-null {@link AudioMixingRule} instance. 280 * @throws IllegalArgumentException 281 */ Builder(AudioMixingRule rule)282 public Builder(AudioMixingRule rule) 283 throws IllegalArgumentException { 284 if (rule == null) { 285 throw new IllegalArgumentException("Illegal null AudioMixingRule argument"); 286 } 287 mRule = rule; 288 } 289 290 /** 291 * @hide 292 * Only used by AudioPolicyConfig, not a public API. 293 * @param rule 294 * @return the same Builder instance. 295 * @throws IllegalArgumentException 296 */ setMixingRule(AudioMixingRule rule)297 Builder setMixingRule(AudioMixingRule rule) 298 throws IllegalArgumentException { 299 if (rule == null) { 300 throw new IllegalArgumentException("Illegal null AudioMixingRule argument"); 301 } 302 mRule = rule; 303 return this; 304 } 305 306 /** 307 * @hide 308 * Only used by AudioPolicyConfig, not a public API. 309 * @param callbackFlags which callbacks are called from native 310 * @return the same Builder instance. 311 * @throws IllegalArgumentException 312 */ setCallbackFlags(int flags)313 Builder setCallbackFlags(int flags) throws IllegalArgumentException { 314 if ((flags != 0) && ((flags & CALLBACK_FLAGS_ALL) == 0)) { 315 throw new IllegalArgumentException("Illegal callback flags 0x" 316 + Integer.toHexString(flags).toUpperCase()); 317 } 318 mCallbackFlags = flags; 319 return this; 320 } 321 322 /** 323 * @hide 324 * Only used by AudioPolicyConfig, not a public API. 325 * @param deviceType an AudioSystem.DEVICE_* value, not AudioDeviceInfo.TYPE_* 326 * @param address 327 * @return the same Builder instance. 328 */ setDevice(int deviceType, String address)329 Builder setDevice(int deviceType, String address) { 330 mDeviceSystemType = deviceType; 331 mDeviceAddress = address; 332 return this; 333 } 334 335 /** 336 * Sets the {@link AudioFormat} for the mix. 337 * @param format a non-null {@link AudioFormat} instance. 338 * @return the same Builder instance. 339 * @throws IllegalArgumentException 340 */ setFormat(AudioFormat format)341 public Builder setFormat(AudioFormat format) 342 throws IllegalArgumentException { 343 if (format == null) { 344 throw new IllegalArgumentException("Illegal null AudioFormat argument"); 345 } 346 mFormat = format; 347 return this; 348 } 349 350 /** 351 * Sets the routing behavior for the mix. If not set, routing behavior will default to 352 * {@link AudioMix#ROUTE_FLAG_LOOP_BACK}. 353 * @param routeFlags one of {@link AudioMix#ROUTE_FLAG_LOOP_BACK}, 354 * {@link AudioMix#ROUTE_FLAG_RENDER} 355 * @return the same Builder instance. 356 * @throws IllegalArgumentException 357 */ setRouteFlags(@outeFlags int routeFlags)358 public Builder setRouteFlags(@RouteFlags int routeFlags) 359 throws IllegalArgumentException { 360 if (routeFlags == 0) { 361 throw new IllegalArgumentException("Illegal empty route flags"); 362 } 363 if ((routeFlags & ROUTE_FLAG_SUPPORTED) == 0) { 364 throw new IllegalArgumentException("Invalid route flags 0x" 365 + Integer.toHexString(routeFlags) + "when configuring an AudioMix"); 366 } 367 if ((routeFlags & ~ROUTE_FLAG_SUPPORTED) != 0) { 368 throw new IllegalArgumentException("Unknown route flags 0x" 369 + Integer.toHexString(routeFlags) + "when configuring an AudioMix"); 370 } 371 mRouteFlags = routeFlags; 372 return this; 373 } 374 375 /** 376 * Sets the audio device used for playback. Cannot be used in the context of an audio 377 * policy used to inject audio to be recorded, or in a mix whose route flags doesn't 378 * specify {@link AudioMix#ROUTE_FLAG_RENDER}. 379 * @param device a non-null AudioDeviceInfo describing the audio device to play the output 380 * of this mix. 381 * @return the same Builder instance 382 * @throws IllegalArgumentException 383 */ setDevice(@onNull AudioDeviceInfo device)384 public Builder setDevice(@NonNull AudioDeviceInfo device) throws IllegalArgumentException { 385 if (device == null) { 386 throw new IllegalArgumentException("Illegal null AudioDeviceInfo argument"); 387 } 388 if (!device.isSink()) { 389 throw new IllegalArgumentException("Unsupported device type on mix, not a sink"); 390 } 391 mDeviceSystemType = AudioDeviceInfo.convertDeviceTypeToInternalDevice(device.getType()); 392 mDeviceAddress = device.getAddress(); 393 return this; 394 } 395 396 /** 397 * Combines all of the settings and return a new {@link AudioMix} object. 398 * @return a new {@link AudioMix} object 399 * @throws IllegalArgumentException if no {@link AudioMixingRule} has been set. 400 */ build()401 public AudioMix build() throws IllegalArgumentException { 402 if (mRule == null) { 403 throw new IllegalArgumentException("Illegal null AudioMixingRule"); 404 } 405 if (mRouteFlags == 0) { 406 // no route flags set, use default as described in Builder.setRouteFlags(int) 407 mRouteFlags = ROUTE_FLAG_LOOP_BACK; 408 } 409 if (mFormat == null) { 410 // FIXME Can we eliminate this? Will AudioMix work with an unspecified sample rate? 411 int rate = AudioSystem.getPrimaryOutputSamplingRate(); 412 if (rate <= 0) { 413 rate = 44100; 414 } 415 mFormat = new AudioFormat.Builder().setSampleRate(rate).build(); 416 } 417 if ((mDeviceSystemType != AudioSystem.DEVICE_NONE) 418 && (mDeviceSystemType != AudioSystem.DEVICE_OUT_REMOTE_SUBMIX) 419 && (mDeviceSystemType != AudioSystem.DEVICE_IN_REMOTE_SUBMIX)) { 420 if ((mRouteFlags & ROUTE_FLAG_RENDER) == 0) { 421 throw new IllegalArgumentException( 422 "Can't have audio device without flag ROUTE_FLAG_RENDER"); 423 } 424 if (mRule.getTargetMixType() != AudioMix.MIX_TYPE_PLAYERS) { 425 throw new IllegalArgumentException("Unsupported device on non-playback mix"); 426 } 427 } else { 428 if ((mRouteFlags & ROUTE_FLAG_SUPPORTED) == ROUTE_FLAG_RENDER) { 429 throw new IllegalArgumentException( 430 "Can't have flag ROUTE_FLAG_RENDER without an audio device"); 431 } 432 if ((mRouteFlags & ROUTE_FLAG_LOOP_BACK) == ROUTE_FLAG_LOOP_BACK) { 433 if (mRule.getTargetMixType() == MIX_TYPE_PLAYERS) { 434 mDeviceSystemType = AudioSystem.DEVICE_OUT_REMOTE_SUBMIX; 435 } else if (mRule.getTargetMixType() == MIX_TYPE_RECORDERS) { 436 mDeviceSystemType = AudioSystem.DEVICE_IN_REMOTE_SUBMIX; 437 } else { 438 throw new IllegalArgumentException("Unknown mixing rule type"); 439 } 440 } 441 } 442 if (mRule.allowPrivilegedPlaybackCapture()) { 443 String error = AudioMix.canBeUsedForPrivilegedCapture(mFormat); 444 if (error != null) { 445 throw new IllegalArgumentException(error); 446 } 447 } 448 return new AudioMix(mRule, mFormat, mRouteFlags, mCallbackFlags, mDeviceSystemType, 449 mDeviceAddress); 450 } 451 } 452 } 453