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 static android.media.AudioSystem.getDeviceName; 20 import static android.media.AudioSystem.isRemoteSubmixDevice; 21 22 import android.annotation.IntDef; 23 import android.annotation.NonNull; 24 import android.annotation.Nullable; 25 import android.annotation.SystemApi; 26 import android.compat.annotation.UnsupportedAppUsage; 27 import android.content.Context; 28 import android.media.AudioDeviceInfo; 29 import android.media.AudioFormat; 30 import android.media.AudioSystem; 31 import android.os.Binder; 32 import android.os.Build; 33 import android.os.IBinder; 34 import android.os.Parcel; 35 import android.os.Parcelable; 36 37 import com.android.internal.annotations.VisibleForTesting; 38 39 import java.lang.annotation.Retention; 40 import java.lang.annotation.RetentionPolicy; 41 import java.util.Objects; 42 43 /** 44 * @hide 45 */ 46 @SystemApi 47 public class AudioMix implements Parcelable { 48 49 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 50 private @NonNull AudioMixingRule mRule; 51 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 52 private @NonNull AudioFormat mFormat; 53 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 54 private int mRouteFlags; 55 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 56 private int mMixType = MIX_TYPE_INVALID; 57 58 private final IBinder mToken; 59 60 // written by AudioPolicy 61 int mMixState = MIX_STATE_DISABLED; 62 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 63 int mCallbackFlags; 64 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 65 @NonNull String mDeviceAddress; 66 67 // initialized in constructor, read by AudioPolicyConfig 68 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 69 final int mDeviceSystemType; // an AudioSystem.DEVICE_* value, not AudioDeviceInfo.TYPE_* 70 71 // The (virtual) device ID that this AudioMix was registered for. This value is overwritten 72 // when registering this AudioMix with an AudioPolicy or attaching this AudioMix to an 73 // AudioPolicy to match the AudioPolicy attribution. Does not imply that it only modifies 74 // audio routing for this device ID. 75 private int mVirtualDeviceId; 76 77 /** 78 * All parameters are guaranteed valid through the Builder. 79 */ AudioMix(@onNull AudioMixingRule rule, @NonNull AudioFormat format, int routeFlags, int callbackFlags, int deviceType, @Nullable String deviceAddress, IBinder token, int virtualDeviceId)80 private AudioMix(@NonNull AudioMixingRule rule, @NonNull AudioFormat format, 81 int routeFlags, int callbackFlags, 82 int deviceType, @Nullable String deviceAddress, IBinder token, 83 int virtualDeviceId) { 84 mRule = Objects.requireNonNull(rule); 85 mFormat = Objects.requireNonNull(format); 86 mRouteFlags = routeFlags; 87 mMixType = rule.getTargetMixType(); 88 mCallbackFlags = callbackFlags; 89 mDeviceSystemType = deviceType; 90 mDeviceAddress = (deviceAddress == null) ? new String("") : deviceAddress; 91 mToken = token; 92 mVirtualDeviceId = virtualDeviceId; 93 } 94 95 // CALLBACK_FLAG_* values: keep in sync with AudioMix::kCbFlag* values defined 96 // in frameworks/av/include/media/AudioPolicy.h 97 /** @hide */ 98 public final static int CALLBACK_FLAG_NOTIFY_ACTIVITY = 0x1; 99 // when adding new MIX_FLAG_* flags, add them to this mask of authorized masks: 100 private final static int CALLBACK_FLAGS_ALL = CALLBACK_FLAG_NOTIFY_ACTIVITY; 101 102 // ROUTE_FLAG_* values: keep in sync with MIX_ROUTE_FLAG_* values defined 103 // in frameworks/av/include/media/AudioPolicy.h 104 /** 105 * An audio mix behavior where the output of the mix is sent to the original destination of 106 * the audio signal, i.e. an output device for an output mix, or a recording for an input mix. 107 */ 108 public static final int ROUTE_FLAG_RENDER = 0x1; 109 /** 110 * An audio mix behavior where the output of the mix is rerouted back to the framework and 111 * is accessible for injection or capture through the {@link AudioTrack} and {@link AudioRecord} 112 * APIs. 113 */ 114 public static final int ROUTE_FLAG_LOOP_BACK = 0x1 << 1; 115 116 /** 117 * An audio mix behavior where the targeted audio is played unaffected but a copy is 118 * accessible for capture through {@link AudioRecord}. 119 * 120 * Only capture of playback is supported, not capture of capture. 121 * Use concurrent capture instead to capture what is captured by other apps. 122 * 123 * The captured audio is an approximation of the played audio. 124 * Effects and volume are not applied, and track are mixed with different delay then in the HAL. 125 * As a result, this API is not suitable for echo cancelling. 126 * @hide 127 */ 128 public static final int ROUTE_FLAG_LOOP_BACK_RENDER = ROUTE_FLAG_LOOP_BACK | ROUTE_FLAG_RENDER; 129 130 private static final int ROUTE_FLAG_SUPPORTED = ROUTE_FLAG_RENDER | ROUTE_FLAG_LOOP_BACK; 131 132 // MIX_TYPE_* values to keep in sync with frameworks/av/include/media/AudioPolicy.h 133 /** 134 * @hide 135 * Invalid mix type, default value. 136 */ 137 public static final int MIX_TYPE_INVALID = -1; 138 /** 139 * @hide 140 * Mix type indicating playback streams are mixed. 141 */ 142 public static final int MIX_TYPE_PLAYERS = 0; 143 /** 144 * @hide 145 * Mix type indicating recording streams are mixed. 146 */ 147 public static final int MIX_TYPE_RECORDERS = 1; 148 149 150 // MIX_STATE_* values to keep in sync with frameworks/av/include/media/AudioPolicy.h 151 /** 152 * State of a mix before its policy is enabled. 153 */ 154 public static final int MIX_STATE_DISABLED = -1; 155 /** 156 * State of a mix when there is no audio to mix. 157 */ 158 public static final int MIX_STATE_IDLE = 0; 159 /** 160 * State of a mix that is actively mixing audio. 161 */ 162 public static final int MIX_STATE_MIXING = 1; 163 164 /** Maximum sampling rate for privileged playback capture*/ 165 private static final int PRIVILEDGED_CAPTURE_MAX_SAMPLE_RATE = 16000; 166 167 /** Maximum channel number for privileged playback capture*/ 168 private static final int PRIVILEDGED_CAPTURE_MAX_CHANNEL_NUMBER = 1; 169 170 /** Maximum channel number for privileged playback capture*/ 171 private static final int PRIVILEDGED_CAPTURE_MAX_BYTES_PER_SAMPLE = 2; 172 173 /** 174 * The current mixing state. 175 * @return one of {@link #MIX_STATE_DISABLED}, {@link #MIX_STATE_IDLE}, 176 * {@link #MIX_STATE_MIXING}. 177 */ getMixState()178 public int getMixState() { 179 return mMixState; 180 } 181 182 183 /** @hide */ getRouteFlags()184 public int getRouteFlags() { 185 return mRouteFlags; 186 } 187 188 /** @hide */ getFormat()189 public AudioFormat getFormat() { 190 return mFormat; 191 } 192 193 /** @hide */ getRule()194 public AudioMixingRule getRule() { 195 return mRule; 196 } 197 198 /** @hide */ getMixType()199 public int getMixType() { 200 return mMixType; 201 } 202 setRegistration(String regId)203 void setRegistration(String regId) { 204 mDeviceAddress = regId; 205 } 206 207 /** @hide */ setAudioMixingRule(@onNull AudioMixingRule rule)208 public void setAudioMixingRule(@NonNull AudioMixingRule rule) { 209 if (mRule.getTargetMixType() != rule.getTargetMixType()) { 210 throw new UnsupportedOperationException( 211 "Target mix role of updated rule doesn't match the mix role of the AudioMix"); 212 } 213 mRule = Objects.requireNonNull(rule); 214 } 215 216 /** @hide */ getRegistration()217 public String getRegistration() { 218 return mDeviceAddress; 219 } 220 221 /** @hide */ isAffectingUsage(int usage)222 public boolean isAffectingUsage(int usage) { 223 return mRule.isAffectingUsage(usage); 224 } 225 226 /** 227 * Returns {@code true} if the rule associated with this mix contains a 228 * RULE_MATCH_ATTRIBUTE_USAGE criterion for the given usage 229 * 230 * @hide 231 */ containsMatchAttributeRuleForUsage(int usage)232 public boolean containsMatchAttributeRuleForUsage(int usage) { 233 return mRule.containsMatchAttributeRuleForUsage(usage); 234 } 235 236 /** @hide */ isRoutedToDevice(int deviceType, @NonNull String deviceAddress)237 public boolean isRoutedToDevice(int deviceType, @NonNull String deviceAddress) { 238 if ((mRouteFlags & ROUTE_FLAG_RENDER) != ROUTE_FLAG_RENDER) { 239 return false; 240 } 241 if (deviceType != mDeviceSystemType) { 242 return false; 243 } 244 if (!deviceAddress.equals(mDeviceAddress)) { 245 return false; 246 } 247 return true; 248 } 249 250 /** @return an error string if the format would not allow Privileged playbackCapture 251 * null otherwise 252 * @hide */ canBeUsedForPrivilegedMediaCapture(AudioFormat format)253 public static String canBeUsedForPrivilegedMediaCapture(AudioFormat format) { 254 int sampleRate = format.getSampleRate(); 255 if (sampleRate > PRIVILEDGED_CAPTURE_MAX_SAMPLE_RATE || sampleRate <= 0) { 256 return "Privileged audio capture sample rate " + sampleRate 257 + " can not be over " + PRIVILEDGED_CAPTURE_MAX_SAMPLE_RATE + "kHz"; 258 } 259 int channelCount = format.getChannelCount(); 260 if (channelCount > PRIVILEDGED_CAPTURE_MAX_CHANNEL_NUMBER || channelCount <= 0) { 261 return "Privileged audio capture channel count " + channelCount + " can not be over " 262 + PRIVILEDGED_CAPTURE_MAX_CHANNEL_NUMBER; 263 } 264 int encoding = format.getEncoding(); 265 if (!format.isPublicEncoding(encoding) || !format.isEncodingLinearPcm(encoding)) { 266 return "Privileged audio capture encoding " + encoding + "is not linear"; 267 } 268 if (format.getBytesPerSample(encoding) > PRIVILEDGED_CAPTURE_MAX_BYTES_PER_SAMPLE) { 269 return "Privileged audio capture encoding " + encoding + " can not be over " 270 + PRIVILEDGED_CAPTURE_MAX_BYTES_PER_SAMPLE + " bytes per sample"; 271 } 272 return null; 273 } 274 275 /** @hide */ isForCallRedirection()276 public boolean isForCallRedirection() { 277 return mRule.isForCallRedirection(); 278 } 279 280 /** @hide */ matchesVirtualDeviceId(int deviceId)281 public boolean matchesVirtualDeviceId(int deviceId) { 282 return mVirtualDeviceId == deviceId; 283 } 284 285 /** @hide */ 286 @Override equals(Object o)287 public boolean equals(Object o) { 288 if (this == o) return true; 289 if (o == null || getClass() != o.getClass()) return false; 290 291 final AudioMix that = (AudioMix) o; 292 boolean tokenMatch = android.media.audiopolicy.Flags.audioMixOwnership() 293 ? Objects.equals(this.mToken, that.mToken) 294 : true; 295 return Objects.equals(this.mRouteFlags, that.mRouteFlags) 296 && Objects.equals(this.mRule, that.mRule) 297 && Objects.equals(this.mMixType, that.mMixType) 298 && Objects.equals(this.mFormat, that.mFormat) 299 && tokenMatch; 300 } 301 302 /** @hide */ 303 @Override hashCode()304 public int hashCode() { 305 if (android.media.audiopolicy.Flags.audioMixOwnership()) { 306 return Objects.hash(mRouteFlags, mRule, mMixType, mFormat, mToken); 307 } 308 return Objects.hash(mRouteFlags, mRule, mMixType, mFormat); 309 } 310 311 @Override describeContents()312 public int describeContents() { 313 return 0; 314 } 315 316 @Override writeToParcel(@onNull Parcel dest, int flags)317 public void writeToParcel(@NonNull Parcel dest, int flags) { 318 // write mix route flags 319 dest.writeInt(mRouteFlags); 320 // write callback flags 321 dest.writeInt(mCallbackFlags); 322 // write device information 323 dest.writeInt(mDeviceSystemType); 324 dest.writeString8(mDeviceAddress); 325 mFormat.writeToParcel(dest, flags); 326 mRule.writeToParcel(dest, flags); 327 dest.writeStrongBinder(mToken); 328 dest.writeInt(mVirtualDeviceId); 329 } 330 331 public static final @NonNull Parcelable.Creator<AudioMix> CREATOR = new Parcelable.Creator<>() { 332 /** 333 * Rebuilds an AudioMix previously stored with writeToParcel(). 334 * 335 * @param p Parcel object to read the AudioMix from 336 * @return a new AudioMix created from the data in the parcel 337 */ 338 public AudioMix createFromParcel(Parcel p) { 339 final AudioMix.Builder mixBuilder = new AudioMix.Builder(); 340 // read mix route flags 341 mixBuilder.setRouteFlags(p.readInt()); 342 // read callback flags 343 mixBuilder.setCallbackFlags(p.readInt()); 344 // read device information 345 mixBuilder.setDevice(p.readInt(), p.readString8()); 346 mixBuilder.setFormat(AudioFormat.CREATOR.createFromParcel(p)); 347 mixBuilder.setMixingRule(AudioMixingRule.CREATOR.createFromParcel(p)); 348 mixBuilder.setToken(p.readStrongBinder()); 349 mixBuilder.setVirtualDeviceId(p.readInt()); 350 return mixBuilder.build(); 351 } 352 353 public AudioMix[] newArray(int size) { 354 return new AudioMix[size]; 355 } 356 }; 357 358 /** 359 * Updates the deviceId of the AudioMix to match with the AudioPolicy the mix is registered 360 * through. 361 * @hide 362 */ setVirtualDeviceId(int virtualDeviceId)363 public void setVirtualDeviceId(int virtualDeviceId) { 364 mVirtualDeviceId = virtualDeviceId; 365 } 366 367 /** @hide */ 368 @IntDef(flag = true, 369 value = { ROUTE_FLAG_RENDER, ROUTE_FLAG_LOOP_BACK } ) 370 @Retention(RetentionPolicy.SOURCE) 371 public @interface RouteFlags {} 372 373 /** 374 * Builder class for {@link AudioMix} objects 375 */ 376 public static class Builder { 377 private AudioMixingRule mRule = null; 378 private AudioFormat mFormat = null; 379 private int mRouteFlags = 0; 380 private int mCallbackFlags = 0; 381 private IBinder mToken = null; 382 private int mVirtualDeviceId = Context.DEVICE_ID_DEFAULT; 383 // an AudioSystem.DEVICE_* value, not AudioDeviceInfo.TYPE_* 384 private int mDeviceSystemType = AudioSystem.DEVICE_NONE; 385 private String mDeviceAddress = null; 386 387 /** 388 * @hide 389 * Only used by AudioPolicyConfig, not a public API. 390 */ Builder()391 Builder() { } 392 393 /** 394 * Construct an instance for the given {@link AudioMixingRule}. 395 * @param rule a non-null {@link AudioMixingRule} instance. 396 * @throws IllegalArgumentException 397 */ Builder(@onNull AudioMixingRule rule)398 public Builder(@NonNull AudioMixingRule rule) 399 throws IllegalArgumentException { 400 if (rule == null) { 401 throw new IllegalArgumentException("Illegal null AudioMixingRule argument"); 402 } 403 mRule = rule; 404 } 405 406 /** 407 * @hide 408 * Only used by AudioPolicyConfig, not a public API. 409 * @param rule 410 * @return the same Builder instance. 411 * @throws IllegalArgumentException 412 */ setMixingRule(@onNull AudioMixingRule rule)413 Builder setMixingRule(@NonNull AudioMixingRule rule) 414 throws IllegalArgumentException { 415 if (rule == null) { 416 throw new IllegalArgumentException("Illegal null AudioMixingRule argument"); 417 } 418 mRule = rule; 419 return this; 420 } 421 422 /** 423 * @hide 424 * Only used by AudioMix internally. 425 */ setToken(IBinder token)426 Builder setToken(IBinder token) { 427 mToken = token; 428 return this; 429 } 430 431 /** 432 * @hide 433 * Only used by AudioMix internally. 434 */ setVirtualDeviceId(int virtualDeviceId)435 Builder setVirtualDeviceId(int virtualDeviceId) { 436 mVirtualDeviceId = virtualDeviceId; 437 return this; 438 } 439 440 /** 441 * @hide 442 * Only used by AudioPolicyConfig, not a public API. 443 * @param callbackFlags which callbacks are called from native 444 * @return the same Builder instance. 445 * @throws IllegalArgumentException 446 */ setCallbackFlags(int flags)447 Builder setCallbackFlags(int flags) throws IllegalArgumentException { 448 if ((flags != 0) && ((flags & CALLBACK_FLAGS_ALL) == 0)) { 449 throw new IllegalArgumentException("Illegal callback flags 0x" 450 + Integer.toHexString(flags).toUpperCase()); 451 } 452 mCallbackFlags = flags; 453 return this; 454 } 455 456 /** 457 * @hide 458 * Only used by AudioPolicyConfig, not a public API. 459 * @param deviceType an AudioSystem.DEVICE_* value, not AudioDeviceInfo.TYPE_* 460 * @param address 461 * @return the same Builder instance. 462 */ 463 @VisibleForTesting setDevice(int deviceType, String address)464 public Builder setDevice(int deviceType, String address) { 465 mDeviceSystemType = deviceType; 466 mDeviceAddress = address; 467 return this; 468 } 469 470 /** 471 * Sets the {@link AudioFormat} for the mix. 472 * @param format a non-null {@link AudioFormat} instance. 473 * @return the same Builder instance. 474 * @throws IllegalArgumentException 475 */ setFormat(@onNull AudioFormat format)476 public Builder setFormat(@NonNull AudioFormat format) 477 throws IllegalArgumentException { 478 if (format == null) { 479 throw new IllegalArgumentException("Illegal null AudioFormat argument"); 480 } 481 mFormat = format; 482 return this; 483 } 484 485 /** 486 * Sets the routing behavior for the mix. If not set, routing behavior will default to 487 * {@link AudioMix#ROUTE_FLAG_LOOP_BACK}. 488 * @param routeFlags one of {@link AudioMix#ROUTE_FLAG_LOOP_BACK}, 489 * {@link AudioMix#ROUTE_FLAG_RENDER} 490 * @return the same Builder instance. 491 * @throws IllegalArgumentException 492 */ setRouteFlags(@outeFlags int routeFlags)493 public Builder setRouteFlags(@RouteFlags int routeFlags) 494 throws IllegalArgumentException { 495 if (routeFlags == 0) { 496 throw new IllegalArgumentException("Illegal empty route flags"); 497 } 498 if ((routeFlags & ROUTE_FLAG_SUPPORTED) == 0) { 499 throw new IllegalArgumentException("Invalid route flags 0x" 500 + Integer.toHexString(routeFlags) + "when configuring an AudioMix"); 501 } 502 if ((routeFlags & ~ROUTE_FLAG_SUPPORTED) != 0) { 503 throw new IllegalArgumentException("Unknown route flags 0x" 504 + Integer.toHexString(routeFlags) + "when configuring an AudioMix"); 505 } 506 mRouteFlags = routeFlags; 507 return this; 508 } 509 510 /** 511 * Sets the audio device used for playback. Cannot be used in the context of an audio 512 * policy used to inject audio to be recorded, or in a mix whose route flags doesn't 513 * specify {@link AudioMix#ROUTE_FLAG_RENDER}. 514 * @param device a non-null AudioDeviceInfo describing the audio device to play the output 515 * of this mix. 516 * @return the same Builder instance 517 * @throws IllegalArgumentException 518 */ setDevice(@onNull AudioDeviceInfo device)519 public Builder setDevice(@NonNull AudioDeviceInfo device) throws IllegalArgumentException { 520 if (device == null) { 521 throw new IllegalArgumentException("Illegal null AudioDeviceInfo argument"); 522 } 523 if (!device.isSink()) { 524 throw new IllegalArgumentException("Unsupported device type on mix, not a sink"); 525 } 526 mDeviceSystemType = AudioDeviceInfo.convertDeviceTypeToInternalDevice(device.getType()); 527 mDeviceAddress = device.getAddress(); 528 return this; 529 } 530 531 /** 532 * Combines all of the settings and return a new {@link AudioMix} object. 533 * @return a new {@link AudioMix} object 534 * @throws IllegalArgumentException if no {@link AudioMixingRule} has been set. 535 */ build()536 public AudioMix build() throws IllegalArgumentException { 537 if (mRule == null) { 538 throw new IllegalArgumentException("Illegal null AudioMixingRule"); 539 } 540 if (mRouteFlags == 0) { 541 // no route flags set, use default as described in Builder.setRouteFlags(int) 542 mRouteFlags = ROUTE_FLAG_LOOP_BACK; 543 } 544 if (mFormat == null) { 545 // FIXME Can we eliminate this? Will AudioMix work with an unspecified sample rate? 546 int rate = AudioSystem.getPrimaryOutputSamplingRate(); 547 if (rate <= 0) { 548 rate = 44100; 549 } 550 mFormat = new AudioFormat.Builder().setSampleRate(rate).build(); 551 } else { 552 // Ensure that 'mFormat' uses an output channel mask. Using an input channel 553 // mask was not made 'illegal' initially, however the framework code 554 // assumes usage in AudioMixes of output channel masks only (b/194910301). 555 if ((mFormat.getPropertySetMask() 556 & AudioFormat.AUDIO_FORMAT_HAS_PROPERTY_CHANNEL_MASK) != 0) { 557 if (mFormat.getChannelCount() == 1 558 && mFormat.getChannelMask() == AudioFormat.CHANNEL_IN_MONO) { 559 mFormat = new AudioFormat.Builder(mFormat).setChannelMask( 560 AudioFormat.CHANNEL_OUT_MONO).build(); 561 } 562 // CHANNEL_IN_STEREO == CHANNEL_OUT_STEREO so no need to correct. 563 // CHANNEL_IN_FRONT_BACK is hidden, should not appear. 564 } 565 } 566 567 if ((mRouteFlags & ROUTE_FLAG_LOOP_BACK) == ROUTE_FLAG_LOOP_BACK) { 568 if (mDeviceSystemType == AudioSystem.DEVICE_NONE) { 569 // If there was no device type explicitly set, configure it based on mix type. 570 mDeviceSystemType = getLoopbackDeviceSystemTypeForAudioMixingRule(mRule); 571 } else if (!isRemoteSubmixDevice(mDeviceSystemType)) { 572 // Loopback mode only supports remote submix devices. 573 throw new IllegalArgumentException("Device " + getDeviceName(mDeviceSystemType) 574 + "is not supported for loopback mix."); 575 } 576 } 577 578 if ((mRouteFlags & ROUTE_FLAG_RENDER) == ROUTE_FLAG_RENDER) { 579 if (mDeviceSystemType == AudioSystem.DEVICE_NONE) { 580 throw new IllegalArgumentException( 581 "Can't have flag ROUTE_FLAG_RENDER without an audio device"); 582 } 583 584 if (AudioSystem.DEVICE_IN_ALL_SET.contains(mDeviceSystemType)) { 585 throw new IllegalArgumentException( 586 "Input device is not supported with ROUTE_FLAG_RENDER"); 587 } 588 589 if (mRule.getTargetMixType() == MIX_TYPE_RECORDERS) { 590 throw new IllegalArgumentException( 591 "ROUTE_FLAG_RENDER/ROUTE_FLAG_LOOP_BACK_RENDER is not supported for " 592 + "non-playback mix rule"); 593 } 594 } 595 596 if (mRule.allowPrivilegedMediaPlaybackCapture()) { 597 String error = AudioMix.canBeUsedForPrivilegedMediaCapture(mFormat); 598 if (error != null) { 599 throw new IllegalArgumentException(error); 600 } 601 } 602 603 if (mToken == null) { 604 mToken = new Binder(); 605 } 606 607 return new AudioMix(mRule, mFormat, mRouteFlags, mCallbackFlags, mDeviceSystemType, 608 mDeviceAddress, mToken, mVirtualDeviceId); 609 } 610 getLoopbackDeviceSystemTypeForAudioMixingRule(AudioMixingRule rule)611 private int getLoopbackDeviceSystemTypeForAudioMixingRule(AudioMixingRule rule) { 612 switch (mRule.getTargetMixType()) { 613 case MIX_TYPE_PLAYERS: 614 return AudioSystem.DEVICE_OUT_REMOTE_SUBMIX; 615 case MIX_TYPE_RECORDERS: 616 return AudioSystem.DEVICE_IN_REMOTE_SUBMIX; 617 default: 618 throw new IllegalArgumentException( 619 "Unknown mixing rule type - 0x" + Integer.toHexString( 620 rule.getTargetMixType())); 621 } 622 } 623 } 624 } 625