Home | History | Annotate | Download | only in telecom
      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.telecom;
     18 
     19 import android.annotation.IntDef;
     20 import android.annotation.NonNull;
     21 import android.annotation.Nullable;
     22 import android.annotation.TestApi;
     23 import android.bluetooth.BluetoothDevice;
     24 import android.os.Parcel;
     25 import android.os.Parcelable;
     26 
     27 import java.lang.annotation.Retention;
     28 import java.lang.annotation.RetentionPolicy;
     29 import java.util.ArrayList;
     30 import java.util.Arrays;
     31 import java.util.Collection;
     32 import java.util.Collections;
     33 import java.util.List;
     34 import java.util.Locale;
     35 import java.util.Objects;
     36 import java.util.stream.Collectors;
     37 
     38 /**
     39  *  Encapsulates the telecom audio state, including the current audio routing, supported audio
     40  *  routing and mute.
     41  */
     42 public final class CallAudioState implements Parcelable {
     43     /** @hide */
     44     @Retention(RetentionPolicy.SOURCE)
     45     @IntDef(value={ROUTE_EARPIECE, ROUTE_BLUETOOTH, ROUTE_WIRED_HEADSET, ROUTE_SPEAKER},
     46             flag=true)
     47     public @interface CallAudioRoute {}
     48 
     49     /** Direct the audio stream through the device's earpiece. */
     50     public static final int ROUTE_EARPIECE      = 0x00000001;
     51 
     52     /** Direct the audio stream through Bluetooth. */
     53     public static final int ROUTE_BLUETOOTH     = 0x00000002;
     54 
     55     /** Direct the audio stream through a wired headset. */
     56     public static final int ROUTE_WIRED_HEADSET = 0x00000004;
     57 
     58     /** Direct the audio stream through the device's speakerphone. */
     59     public static final int ROUTE_SPEAKER       = 0x00000008;
     60 
     61     /**
     62      * Direct the audio stream through the device's earpiece or wired headset if one is
     63      * connected.
     64      */
     65     public static final int ROUTE_WIRED_OR_EARPIECE = ROUTE_EARPIECE | ROUTE_WIRED_HEADSET;
     66 
     67     /**
     68      * Bit mask of all possible audio routes.
     69      *
     70      * @hide
     71      **/
     72     public static final int ROUTE_ALL = ROUTE_EARPIECE | ROUTE_BLUETOOTH | ROUTE_WIRED_HEADSET |
     73             ROUTE_SPEAKER;
     74 
     75     private final boolean isMuted;
     76     private final int route;
     77     private final int supportedRouteMask;
     78     private final BluetoothDevice activeBluetoothDevice;
     79     private final Collection<BluetoothDevice> supportedBluetoothDevices;
     80 
     81     /**
     82      * Constructor for a {@link CallAudioState} object.
     83      *
     84      * @param muted {@code true} if the call is muted, {@code false} otherwise.
     85      * @param route The current audio route being used.
     86      * Allowed values:
     87      * {@link #ROUTE_EARPIECE}
     88      * {@link #ROUTE_BLUETOOTH}
     89      * {@link #ROUTE_WIRED_HEADSET}
     90      * {@link #ROUTE_SPEAKER}
     91      * @param supportedRouteMask Bit mask of all routes supported by this call. This should be a
     92      * bitwise combination of the following values:
     93      * {@link #ROUTE_EARPIECE}
     94      * {@link #ROUTE_BLUETOOTH}
     95      * {@link #ROUTE_WIRED_HEADSET}
     96      * {@link #ROUTE_SPEAKER}
     97      */
     98     public CallAudioState(boolean muted, @CallAudioRoute int route,
     99             @CallAudioRoute int supportedRouteMask) {
    100         this(muted, route, supportedRouteMask, null, Collections.emptyList());
    101     }
    102 
    103     /** @hide */
    104     @TestApi
    105     public CallAudioState(boolean isMuted, @CallAudioRoute int route,
    106             @CallAudioRoute int supportedRouteMask,
    107             @Nullable BluetoothDevice activeBluetoothDevice,
    108             @NonNull Collection<BluetoothDevice> supportedBluetoothDevices) {
    109         this.isMuted = isMuted;
    110         this.route = route;
    111         this.supportedRouteMask = supportedRouteMask;
    112         this.activeBluetoothDevice = activeBluetoothDevice;
    113         this.supportedBluetoothDevices = supportedBluetoothDevices;
    114     }
    115 
    116     /** @hide */
    117     public CallAudioState(CallAudioState state) {
    118         isMuted = state.isMuted();
    119         route = state.getRoute();
    120         supportedRouteMask = state.getSupportedRouteMask();
    121         activeBluetoothDevice = state.activeBluetoothDevice;
    122         supportedBluetoothDevices = state.getSupportedBluetoothDevices();
    123     }
    124 
    125     /** @hide */
    126     @SuppressWarnings("deprecation")
    127     public CallAudioState(AudioState state) {
    128         isMuted = state.isMuted();
    129         route = state.getRoute();
    130         supportedRouteMask = state.getSupportedRouteMask();
    131         activeBluetoothDevice = null;
    132         supportedBluetoothDevices = Collections.emptyList();
    133     }
    134 
    135     @Override
    136     public boolean equals(Object obj) {
    137         if (obj == null) {
    138             return false;
    139         }
    140         if (!(obj instanceof CallAudioState)) {
    141             return false;
    142         }
    143         CallAudioState state = (CallAudioState) obj;
    144         if (supportedBluetoothDevices.size() != state.supportedBluetoothDevices.size()) {
    145             return false;
    146         }
    147         for (BluetoothDevice device : supportedBluetoothDevices) {
    148             if (!state.supportedBluetoothDevices.contains(device)) {
    149                 return false;
    150             }
    151         }
    152         return Objects.equals(activeBluetoothDevice, state.activeBluetoothDevice) && isMuted() ==
    153                 state.isMuted() && getRoute() == state.getRoute() && getSupportedRouteMask() ==
    154                 state.getSupportedRouteMask();
    155     }
    156 
    157     @Override
    158     public String toString() {
    159         String bluetoothDeviceList = supportedBluetoothDevices.stream()
    160                 .map(BluetoothDevice::getAddress).collect(Collectors.joining(", "));
    161 
    162         return String.format(Locale.US,
    163                 "[AudioState isMuted: %b, route: %s, supportedRouteMask: %s, " +
    164                         "activeBluetoothDevice: [%s], supportedBluetoothDevices: [%s]]",
    165                 isMuted,
    166                 audioRouteToString(route),
    167                 audioRouteToString(supportedRouteMask),
    168                 activeBluetoothDevice,
    169                 bluetoothDeviceList);
    170     }
    171 
    172     /**
    173      * @return {@code true} if the call is muted, {@code false} otherwise.
    174      */
    175     public boolean isMuted() {
    176         return isMuted;
    177     }
    178 
    179     /**
    180      * @return The current audio route being used.
    181      */
    182     @CallAudioRoute
    183     public int getRoute() {
    184         return route;
    185     }
    186 
    187     /**
    188      * @return Bit mask of all routes supported by this call.
    189      */
    190     @CallAudioRoute
    191     public int getSupportedRouteMask() {
    192         return supportedRouteMask;
    193     }
    194 
    195     /**
    196      * @return The {@link BluetoothDevice} through which audio is being routed.
    197      *         Will not be {@code null} if {@link #getRoute()} returns {@link #ROUTE_BLUETOOTH}.
    198      */
    199     public BluetoothDevice getActiveBluetoothDevice() {
    200         return activeBluetoothDevice;
    201     }
    202 
    203     /**
    204      * @return {@link List} of {@link BluetoothDevice}s that can be used for this call.
    205      */
    206     public Collection<BluetoothDevice> getSupportedBluetoothDevices() {
    207         return supportedBluetoothDevices;
    208     }
    209 
    210     /**
    211      * Converts the provided audio route into a human readable string representation.
    212      *
    213      * @param route to convert into a string.
    214      *
    215      * @return String representation of the provided audio route.
    216      */
    217     public static String audioRouteToString(int route) {
    218         if (route == 0 || (route & ~ROUTE_ALL) != 0x0) {
    219             return "UNKNOWN";
    220         }
    221 
    222         StringBuffer buffer = new StringBuffer();
    223         if ((route & ROUTE_EARPIECE) == ROUTE_EARPIECE) {
    224             listAppend(buffer, "EARPIECE");
    225         }
    226         if ((route & ROUTE_BLUETOOTH) == ROUTE_BLUETOOTH) {
    227             listAppend(buffer, "BLUETOOTH");
    228         }
    229         if ((route & ROUTE_WIRED_HEADSET) == ROUTE_WIRED_HEADSET) {
    230             listAppend(buffer, "WIRED_HEADSET");
    231         }
    232         if ((route & ROUTE_SPEAKER) == ROUTE_SPEAKER) {
    233             listAppend(buffer, "SPEAKER");
    234         }
    235 
    236         return buffer.toString();
    237     }
    238 
    239     /**
    240      * Responsible for creating AudioState objects for deserialized Parcels.
    241      */
    242     public static final Parcelable.Creator<CallAudioState> CREATOR =
    243             new Parcelable.Creator<CallAudioState> () {
    244 
    245         @Override
    246         public CallAudioState createFromParcel(Parcel source) {
    247             boolean isMuted = source.readByte() == 0 ? false : true;
    248             int route = source.readInt();
    249             int supportedRouteMask = source.readInt();
    250             BluetoothDevice activeBluetoothDevice = source.readParcelable(
    251                     ClassLoader.getSystemClassLoader());
    252             List<BluetoothDevice> supportedBluetoothDevices = new ArrayList<>();
    253             source.readParcelableList(supportedBluetoothDevices,
    254                     ClassLoader.getSystemClassLoader());
    255             return new CallAudioState(isMuted, route,
    256                     supportedRouteMask, activeBluetoothDevice, supportedBluetoothDevices);
    257         }
    258 
    259         @Override
    260         public CallAudioState[] newArray(int size) {
    261             return new CallAudioState[size];
    262         }
    263     };
    264 
    265     /**
    266      * {@inheritDoc}
    267      */
    268     @Override
    269     public int describeContents() {
    270         return 0;
    271     }
    272 
    273     /**
    274      * Writes AudioState object into a serializeable Parcel.
    275      */
    276     @Override
    277     public void writeToParcel(Parcel destination, int flags) {
    278         destination.writeByte((byte) (isMuted ? 1 : 0));
    279         destination.writeInt(route);
    280         destination.writeInt(supportedRouteMask);
    281         destination.writeParcelable(activeBluetoothDevice, 0);
    282         destination.writeParcelableList(new ArrayList<>(supportedBluetoothDevices), 0);
    283     }
    284 
    285     private static void listAppend(StringBuffer buffer, String str) {
    286         if (buffer.length() > 0) {
    287             buffer.append(", ");
    288         }
    289         buffer.append(str);
    290     }
    291 }
    292