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