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.NonNull;
     20 import android.media.AudioFormat;
     21 import android.media.AudioManager;
     22 import android.media.AudioPatch;
     23 import android.media.audiopolicy.AudioMixingRule.AudioMixMatchCriterion;
     24 import android.os.Parcel;
     25 import android.os.Parcelable;
     26 import android.util.Log;
     27 
     28 import com.android.internal.annotations.GuardedBy;
     29 
     30 import java.util.ArrayList;
     31 import java.util.Objects;
     32 
     33 /**
     34  * @hide
     35  * Internal storage class for AudioPolicy configuration.
     36  */
     37 public class AudioPolicyConfig implements Parcelable {
     38 
     39     private static final String TAG = "AudioPolicyConfig";
     40 
     41     protected final ArrayList<AudioMix> mMixes;
     42     protected int mDuckingPolicy = AudioPolicy.FOCUS_POLICY_DUCKING_IN_APP;
     43 
     44     private String mRegistrationId = null;
     45 
     46     /** counter for the mixes that are / have been in the list of AudioMix
     47      *  e.g. register 4 mixes (counter is 3), remove 1 (counter is 3), add 1 (counter is 4)
     48      */
     49     private int mMixCounter = 0;
     50 
     51     protected AudioPolicyConfig(AudioPolicyConfig conf) {
     52         mMixes = conf.mMixes;
     53     }
     54 
     55     AudioPolicyConfig(ArrayList<AudioMix> mixes) {
     56         mMixes = mixes;
     57     }
     58 
     59     /**
     60      * Add an {@link AudioMix} to be part of the audio policy being built.
     61      * @param mix a non-null {@link AudioMix} to be part of the audio policy.
     62      * @return the same Builder instance.
     63      * @throws IllegalArgumentException
     64      */
     65     public void addMix(AudioMix mix) throws IllegalArgumentException {
     66         if (mix == null) {
     67             throw new IllegalArgumentException("Illegal null AudioMix argument");
     68         }
     69         mMixes.add(mix);
     70     }
     71 
     72     public ArrayList<AudioMix> getMixes() {
     73         return mMixes;
     74     }
     75 
     76     @Override
     77     public int hashCode() {
     78         return Objects.hash(mMixes);
     79     }
     80 
     81     @Override
     82     public int describeContents() {
     83         return 0;
     84     }
     85 
     86     @Override
     87     public void writeToParcel(Parcel dest, int flags) {
     88         dest.writeInt(mMixes.size());
     89         for (AudioMix mix : mMixes) {
     90             // write mix route flags
     91             dest.writeInt(mix.getRouteFlags());
     92             // write callback flags
     93             dest.writeInt(mix.mCallbackFlags);
     94             // write device information
     95             dest.writeInt(mix.mDeviceSystemType);
     96             dest.writeString(mix.mDeviceAddress);
     97             // write mix format
     98             dest.writeInt(mix.getFormat().getSampleRate());
     99             dest.writeInt(mix.getFormat().getEncoding());
    100             dest.writeInt(mix.getFormat().getChannelMask());
    101             // write mix rules
    102             final ArrayList<AudioMixMatchCriterion> criteria = mix.getRule().getCriteria();
    103             dest.writeInt(criteria.size());
    104             for (AudioMixMatchCriterion criterion : criteria) {
    105                 criterion.writeToParcel(dest);
    106             }
    107         }
    108     }
    109 
    110     private AudioPolicyConfig(Parcel in) {
    111         mMixes = new ArrayList<AudioMix>();
    112         int nbMixes = in.readInt();
    113         for (int i = 0 ; i < nbMixes ; i++) {
    114             final AudioMix.Builder mixBuilder = new AudioMix.Builder();
    115             // read mix route flags
    116             int routeFlags = in.readInt();
    117             mixBuilder.setRouteFlags(routeFlags);
    118             // read callback flags
    119             mixBuilder.setCallbackFlags(in.readInt());
    120             // read device information
    121             mixBuilder.setDevice(in.readInt(), in.readString());
    122             // read mix format
    123             int sampleRate = in.readInt();
    124             int encoding = in.readInt();
    125             int channelMask = in.readInt();
    126             final AudioFormat format = new AudioFormat.Builder().setSampleRate(sampleRate)
    127                     .setChannelMask(channelMask).setEncoding(encoding).build();
    128             mixBuilder.setFormat(format);
    129             // read mix rules
    130             int nbRules = in.readInt();
    131             AudioMixingRule.Builder ruleBuilder = new AudioMixingRule.Builder();
    132             for (int j = 0 ; j < nbRules ; j++) {
    133                 // read the matching rules
    134                 ruleBuilder.addRuleFromParcel(in);
    135             }
    136             mixBuilder.setMixingRule(ruleBuilder.build());
    137             mMixes.add(mixBuilder.build());
    138         }
    139     }
    140 
    141     public static final Parcelable.Creator<AudioPolicyConfig> CREATOR
    142             = new Parcelable.Creator<AudioPolicyConfig>() {
    143         /**
    144          * Rebuilds an AudioPolicyConfig previously stored with writeToParcel().
    145          * @param p Parcel object to read the AudioPolicyConfig from
    146          * @return a new AudioPolicyConfig created from the data in the parcel
    147          */
    148         public AudioPolicyConfig createFromParcel(Parcel p) {
    149             return new AudioPolicyConfig(p);
    150         }
    151         public AudioPolicyConfig[] newArray(int size) {
    152             return new AudioPolicyConfig[size];
    153         }
    154     };
    155 
    156     public String toLogFriendlyString () {
    157         String textDump = new String("android.media.audiopolicy.AudioPolicyConfig:\n");
    158         textDump += mMixes.size() + " AudioMix: "+ mRegistrationId + "\n";
    159         for(AudioMix mix : mMixes) {
    160             // write mix route flags
    161             textDump += "* route flags=0x" + Integer.toHexString(mix.getRouteFlags()) + "\n";
    162             // write mix format
    163             textDump += "  rate=" + mix.getFormat().getSampleRate() + "Hz\n";
    164             textDump += "  encoding=" + mix.getFormat().getEncoding() + "\n";
    165             textDump += "  channels=0x";
    166             textDump += Integer.toHexString(mix.getFormat().getChannelMask()).toUpperCase() +"\n";
    167             // write mix rules
    168             final ArrayList<AudioMixMatchCriterion> criteria = mix.getRule().getCriteria();
    169             for (AudioMixMatchCriterion criterion : criteria) {
    170                 switch(criterion.mRule) {
    171                     case AudioMixingRule.RULE_EXCLUDE_ATTRIBUTE_USAGE:
    172                         textDump += "  exclude usage ";
    173                         textDump += criterion.mAttr.usageToString();
    174                         break;
    175                     case AudioMixingRule.RULE_MATCH_ATTRIBUTE_USAGE:
    176                         textDump += "  match usage ";
    177                         textDump += criterion.mAttr.usageToString();
    178                         break;
    179                     case AudioMixingRule.RULE_EXCLUDE_ATTRIBUTE_CAPTURE_PRESET:
    180                         textDump += "  exclude capture preset ";
    181                         textDump += criterion.mAttr.getCapturePreset();
    182                         break;
    183                     case AudioMixingRule.RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET:
    184                         textDump += "  match capture preset ";
    185                         textDump += criterion.mAttr.getCapturePreset();
    186                         break;
    187                     case AudioMixingRule.RULE_MATCH_UID:
    188                         textDump += "  match UID ";
    189                         textDump += criterion.mIntProp;
    190                         break;
    191                     case AudioMixingRule.RULE_EXCLUDE_UID:
    192                         textDump += "  exclude UID ";
    193                         textDump += criterion.mIntProp;
    194                         break;
    195                     default:
    196                         textDump += "invalid rule!";
    197                 }
    198                 textDump += "\n";
    199             }
    200         }
    201         return textDump;
    202     }
    203 
    204     protected void setRegistration(String regId) {
    205         final boolean currentRegNull = (mRegistrationId == null) || mRegistrationId.isEmpty();
    206         final boolean newRegNull = (regId == null) || regId.isEmpty();
    207         if (!currentRegNull && !newRegNull && !mRegistrationId.equals(regId)) {
    208             Log.e(TAG, "Invalid registration transition from " + mRegistrationId + " to " + regId);
    209             return;
    210         }
    211         mRegistrationId = regId == null ? "" : regId;
    212         for (AudioMix mix : mMixes) {
    213             setMixRegistration(mix);
    214         }
    215     }
    216 
    217     private void setMixRegistration(@NonNull final AudioMix mix) {
    218         if (!mRegistrationId.isEmpty()) {
    219             if ((mix.getRouteFlags() & AudioMix.ROUTE_FLAG_LOOP_BACK) ==
    220                     AudioMix.ROUTE_FLAG_LOOP_BACK) {
    221                 mix.setRegistration(mRegistrationId + "mix" + mixTypeId(mix.getMixType()) + ":"
    222                         + mMixCounter);
    223             } else if ((mix.getRouteFlags() & AudioMix.ROUTE_FLAG_RENDER) ==
    224                     AudioMix.ROUTE_FLAG_RENDER) {
    225                 mix.setRegistration(mix.mDeviceAddress);
    226             }
    227         } else {
    228             mix.setRegistration("");
    229         }
    230         mMixCounter++;
    231     }
    232 
    233     @GuardedBy("mMixes")
    234     protected void add(@NonNull ArrayList<AudioMix> mixes) {
    235         for (AudioMix mix : mixes) {
    236             setMixRegistration(mix);
    237             mMixes.add(mix);
    238         }
    239     }
    240 
    241     @GuardedBy("mMixes")
    242     protected void remove(@NonNull ArrayList<AudioMix> mixes) {
    243         for (AudioMix mix : mixes) {
    244             mMixes.remove(mix);
    245         }
    246     }
    247 
    248     private static String mixTypeId(int type) {
    249         if (type == AudioMix.MIX_TYPE_PLAYERS) return "p";
    250         else if (type == AudioMix.MIX_TYPE_RECORDERS) return "r";
    251         else return "i";
    252     }
    253 
    254     protected String getRegistration() {
    255         return mRegistrationId;
    256     }
    257 }
    258