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