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     public boolean isAffectingUsage(int usage) {
    166         return mRule.isAffectingUsage(usage);
    167     }
    168 
    169     /** @hide */
    170     @Override
    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
    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          */
    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
    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          */
    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          */
    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          */
    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
    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
    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
    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
    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