Home | History | Annotate | Download | only in audiopolicy
      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      */
     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
    133     public int getMixState() {
    134         return mMixState;
    135     }
    136 
    137 
    138     int getRouteFlags() {
    139         return mRouteFlags;
    140     }
    141 
    142     AudioFormat getFormat() {
    143         return mFormat;
    144     }
    145 
    146     AudioMixingRule getRule() {
    147         return mRule;
    148     }
    149 
    150     /** @hide */
    151     public int getMixType() {
    152         return mMixType;
    153     }
    154 
    155     void setRegistration(String regId) {
    156         mDeviceAddress = regId;
    157     }
    158 
    159     /** @hide */
    160     public String getRegistration() {
    161         return mDeviceAddress;
    162     }
    163 
    164     /** @hide */
    165     @Override
    166     public int hashCode() {
    167         return Objects.hash(mRouteFlags, mRule, mMixType, mFormat);
    168     }
    169 
    170     /** @hide */
    171     @IntDef(flag = true,
    172             value = { ROUTE_FLAG_RENDER, ROUTE_FLAG_LOOP_BACK } )
    173     @Retention(RetentionPolicy.SOURCE)
    174     public @interface RouteFlags {}
    175 
    176     /**
    177      * Builder class for {@link AudioMix} objects
    178      *
    179      */
    180     @SystemApi
    181     public static class Builder {
    182         private AudioMixingRule mRule = null;
    183         private AudioFormat mFormat = null;
    184         private int mRouteFlags = 0;
    185         private int mCallbackFlags = 0;
    186         // an AudioSystem.DEVICE_* value, not AudioDeviceInfo.TYPE_*
    187         private int mDeviceSystemType = AudioSystem.DEVICE_NONE;
    188         private String mDeviceAddress = null;
    189 
    190         /**
    191          * @hide
    192          * Only used by AudioPolicyConfig, not a public API.
    193          */
    194         Builder() { }
    195 
    196         /**
    197          * Construct an instance for the given {@link AudioMixingRule}.
    198          * @param rule a non-null {@link AudioMixingRule} instance.
    199          * @throws IllegalArgumentException
    200          */
    201         @SystemApi
    202         public Builder(AudioMixingRule rule)
    203                 throws IllegalArgumentException {
    204             if (rule == null) {
    205                 throw new IllegalArgumentException("Illegal null AudioMixingRule argument");
    206             }
    207             mRule = rule;
    208         }
    209 
    210         /**
    211          * @hide
    212          * Only used by AudioPolicyConfig, not a public API.
    213          * @param rule
    214          * @return the same Builder instance.
    215          * @throws IllegalArgumentException
    216          */
    217         Builder setMixingRule(AudioMixingRule rule)
    218                 throws IllegalArgumentException {
    219             if (rule == null) {
    220                 throw new IllegalArgumentException("Illegal null AudioMixingRule argument");
    221             }
    222             mRule = rule;
    223             return this;
    224         }
    225 
    226         /**
    227          * @hide
    228          * Only used by AudioPolicyConfig, not a public API.
    229          * @param callbackFlags which callbacks are called from native
    230          * @return the same Builder instance.
    231          * @throws IllegalArgumentException
    232          */
    233         Builder setCallbackFlags(int flags) throws IllegalArgumentException {
    234             if ((flags != 0) && ((flags & CALLBACK_FLAGS_ALL) == 0)) {
    235                 throw new IllegalArgumentException("Illegal callback flags 0x"
    236                         + Integer.toHexString(flags).toUpperCase());
    237             }
    238             mCallbackFlags = flags;
    239             return this;
    240         }
    241 
    242         /**
    243          * @hide
    244          * Only used by AudioPolicyConfig, not a public API.
    245          * @param deviceType an AudioSystem.DEVICE_* value, not AudioDeviceInfo.TYPE_*
    246          * @param address
    247          * @return the same Builder instance.
    248          */
    249         Builder setDevice(int deviceType, String address) {
    250             mDeviceSystemType = deviceType;
    251             mDeviceAddress = address;
    252             return this;
    253         }
    254 
    255         /**
    256          * Sets the {@link AudioFormat} for the mix.
    257          * @param format a non-null {@link AudioFormat} instance.
    258          * @return the same Builder instance.
    259          * @throws IllegalArgumentException
    260          */
    261         @SystemApi
    262         public Builder setFormat(AudioFormat format)
    263                 throws IllegalArgumentException {
    264             if (format == null) {
    265                 throw new IllegalArgumentException("Illegal null AudioFormat argument");
    266             }
    267             mFormat = format;
    268             return this;
    269         }
    270 
    271         /**
    272          * Sets the routing behavior for the mix. If not set, routing behavior will default to
    273          * {@link AudioMix#ROUTE_FLAG_LOOP_BACK}.
    274          * @param routeFlags one of {@link AudioMix#ROUTE_FLAG_LOOP_BACK},
    275          *     {@link AudioMix#ROUTE_FLAG_RENDER}
    276          * @return the same Builder instance.
    277          * @throws IllegalArgumentException
    278          */
    279         @SystemApi
    280         public Builder setRouteFlags(@RouteFlags int routeFlags)
    281                 throws IllegalArgumentException {
    282             if (routeFlags == 0) {
    283                 throw new IllegalArgumentException("Illegal empty route flags");
    284             }
    285             if ((routeFlags & ROUTE_FLAG_SUPPORTED) == 0) {
    286                 throw new IllegalArgumentException("Invalid route flags 0x"
    287                         + Integer.toHexString(routeFlags) + "when configuring an AudioMix");
    288             }
    289             if ((routeFlags & ~ROUTE_FLAG_SUPPORTED) != 0) {
    290                 throw new IllegalArgumentException("Unknown route flags 0x"
    291                         + Integer.toHexString(routeFlags) + "when configuring an AudioMix");
    292             }
    293             mRouteFlags = routeFlags;
    294             return this;
    295         }
    296 
    297         /**
    298          * Sets the audio device used for playback. Cannot be used in the context of an audio
    299          * policy used to inject audio to be recorded, or in a mix whose route flags doesn't
    300          * specify {@link AudioMix#ROUTE_FLAG_RENDER}.
    301          * @param device a non-null AudioDeviceInfo describing the audio device to play the output
    302          *     of this mix.
    303          * @return the same Builder instance
    304          * @throws IllegalArgumentException
    305          */
    306         @SystemApi
    307         public Builder setDevice(@NonNull AudioDeviceInfo device) throws IllegalArgumentException {
    308             if (device == null) {
    309                 throw new IllegalArgumentException("Illegal null AudioDeviceInfo argument");
    310             }
    311             if (!device.isSink()) {
    312                 throw new IllegalArgumentException("Unsupported device type on mix, not a sink");
    313             }
    314             mDeviceSystemType = AudioDeviceInfo.convertDeviceTypeToInternalDevice(device.getType());
    315             mDeviceAddress = device.getAddress();
    316             return this;
    317         }
    318 
    319         /**
    320          * Combines all of the settings and return a new {@link AudioMix} object.
    321          * @return a new {@link AudioMix} object
    322          * @throws IllegalArgumentException if no {@link AudioMixingRule} has been set.
    323          */
    324         @SystemApi
    325         public AudioMix build() throws IllegalArgumentException {
    326             if (mRule == null) {
    327                 throw new IllegalArgumentException("Illegal null AudioMixingRule");
    328             }
    329             if (mRouteFlags == 0) {
    330                 // no route flags set, use default as described in Builder.setRouteFlags(int)
    331                 mRouteFlags = ROUTE_FLAG_LOOP_BACK;
    332             }
    333             // can't do loop back AND render at same time in this implementation
    334             if (mRouteFlags == (ROUTE_FLAG_RENDER | ROUTE_FLAG_LOOP_BACK)) {
    335                 throw new IllegalArgumentException("Unsupported route behavior combination 0x" +
    336                         Integer.toHexString(mRouteFlags));
    337             }
    338             if (mFormat == null) {
    339                 // FIXME Can we eliminate this?  Will AudioMix work with an unspecified sample rate?
    340                 int rate = AudioSystem.getPrimaryOutputSamplingRate();
    341                 if (rate <= 0) {
    342                     rate = 44100;
    343                 }
    344                 mFormat = new AudioFormat.Builder().setSampleRate(rate).build();
    345             }
    346             if ((mDeviceSystemType != AudioSystem.DEVICE_NONE)
    347                     && (mDeviceSystemType != AudioSystem.DEVICE_OUT_REMOTE_SUBMIX)
    348                     && (mDeviceSystemType != AudioSystem.DEVICE_IN_REMOTE_SUBMIX)) {
    349                 if ((mRouteFlags & ROUTE_FLAG_RENDER) == 0) {
    350                     throw new IllegalArgumentException(
    351                             "Can't have audio device without flag ROUTE_FLAG_RENDER");
    352                 }
    353                 if (mRule.getTargetMixType() != AudioMix.MIX_TYPE_PLAYERS) {
    354                     throw new IllegalArgumentException("Unsupported device on non-playback mix");
    355                 }
    356             } else {
    357                 if ((mRouteFlags & ROUTE_FLAG_RENDER) == ROUTE_FLAG_RENDER) {
    358                     throw new IllegalArgumentException(
    359                             "Can't have flag ROUTE_FLAG_RENDER without an audio device");
    360                 }
    361                 if ((mRouteFlags & ROUTE_FLAG_SUPPORTED) == ROUTE_FLAG_LOOP_BACK) {
    362                     if (mRule.getTargetMixType() == MIX_TYPE_PLAYERS) {
    363                         mDeviceSystemType = AudioSystem.DEVICE_OUT_REMOTE_SUBMIX;
    364                     } else if (mRule.getTargetMixType() == MIX_TYPE_RECORDERS) {
    365                         mDeviceSystemType = AudioSystem.DEVICE_IN_REMOTE_SUBMIX;
    366                     } else {
    367                         throw new IllegalArgumentException("Unknown mixing rule type");
    368                     }
    369                 }
    370             }
    371             return new AudioMix(mRule, mFormat, mRouteFlags, mCallbackFlags, mDeviceSystemType,
    372                     mDeviceAddress);
    373         }
    374     }
    375 }
    376