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