Home | History | Annotate | Download | only in voicemail
      1 /*
      2  * Copyright (C) 2015 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 com.android.dialer.voicemail;
     18 
     19 import android.content.Context;
     20 import android.media.AudioManager;
     21 import android.media.AudioManager.OnAudioFocusChangeListener;
     22 import android.telecom.CallAudioState;
     23 import android.util.Log;
     24 
     25 import java.util.concurrent.RejectedExecutionException;
     26 
     27 /**
     28  * This class manages all audio changes for voicemail playback.
     29  */
     30 final class VoicemailAudioManager implements OnAudioFocusChangeListener,
     31         WiredHeadsetManager.Listener {
     32     private static final String TAG = VoicemailAudioManager.class.getSimpleName();
     33 
     34     public static final int PLAYBACK_STREAM = AudioManager.STREAM_VOICE_CALL;
     35 
     36     private AudioManager mAudioManager;
     37     private VoicemailPlaybackPresenter mVoicemailPlaybackPresenter;
     38     private WiredHeadsetManager mWiredHeadsetManager;
     39     private boolean mWasSpeakerOn;
     40     private CallAudioState mCallAudioState;
     41 
     42     public VoicemailAudioManager(Context context,
     43             VoicemailPlaybackPresenter voicemailPlaybackPresenter) {
     44         mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
     45         mVoicemailPlaybackPresenter = voicemailPlaybackPresenter;
     46         mWiredHeadsetManager = new WiredHeadsetManager(context);
     47         mWiredHeadsetManager.setListener(this);
     48 
     49         mCallAudioState = getInitialAudioState();
     50         Log.i(TAG, "Initial audioState = " + mCallAudioState);
     51     }
     52 
     53     public void requestAudioFocus() {
     54         int result = mAudioManager.requestAudioFocus(
     55                 this,
     56                 PLAYBACK_STREAM,
     57                 AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
     58         if (result != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
     59             throw new RejectedExecutionException("Could not capture audio focus.");
     60         }
     61     }
     62 
     63     public void abandonAudioFocus() {
     64         mAudioManager.abandonAudioFocus(this);
     65     }
     66 
     67     @Override
     68     public void onAudioFocusChange(int focusChange) {
     69         Log.d(TAG, "onAudioFocusChange: focusChange=" + focusChange);
     70         mVoicemailPlaybackPresenter.onAudioFocusChange(focusChange == AudioManager.AUDIOFOCUS_GAIN);
     71     }
     72 
     73     @Override
     74     public void onWiredHeadsetPluggedInChanged(boolean oldIsPluggedIn, boolean newIsPluggedIn) {
     75         Log.i(TAG, "wired headset was plugged in changed: " + oldIsPluggedIn
     76                 + " -> "+ newIsPluggedIn);
     77 
     78         if (oldIsPluggedIn == newIsPluggedIn) {
     79             return;
     80         }
     81 
     82         int newRoute = mCallAudioState.getRoute();  // start out with existing route
     83         if (newIsPluggedIn) {
     84             newRoute = CallAudioState.ROUTE_WIRED_HEADSET;
     85         } else {
     86             if (mWasSpeakerOn) {
     87                 newRoute = CallAudioState.ROUTE_SPEAKER;
     88             } else {
     89                 newRoute = CallAudioState.ROUTE_EARPIECE;
     90             }
     91         }
     92 
     93         mVoicemailPlaybackPresenter.setSpeakerphoneOn(newRoute == CallAudioState.ROUTE_SPEAKER);
     94 
     95         // We need to call this every time even if we do not change the route because the supported
     96         // routes changed either to include or not include WIRED_HEADSET.
     97         setSystemAudioState(
     98                 new CallAudioState(false /* muted */, newRoute, calculateSupportedRoutes()));
     99     }
    100 
    101     public void setSpeakerphoneOn(boolean on) {
    102         setAudioRoute(on ? CallAudioState.ROUTE_SPEAKER : CallAudioState.ROUTE_WIRED_OR_EARPIECE);
    103     }
    104 
    105     public boolean isWiredHeadsetPluggedIn() {
    106         return mWiredHeadsetManager.isPluggedIn();
    107     }
    108 
    109     public void registerReceivers() {
    110         // Receivers is plural because we expect to add bluetooth support.
    111         mWiredHeadsetManager.registerReceiver();
    112     }
    113 
    114     public void unregisterReceivers() {
    115         mWiredHeadsetManager.unregisterReceiver();
    116     }
    117 
    118     /**
    119      * Change the audio route, for example from earpiece to speakerphone.
    120      *
    121      * @param route The new audio route to use. See {@link CallAudioState}.
    122      */
    123     void setAudioRoute(int route) {
    124         Log.v(TAG, "setAudioRoute, route: " + CallAudioState.audioRouteToString(route));
    125 
    126         // Change ROUTE_WIRED_OR_EARPIECE to a single entry.
    127         int newRoute = selectWiredOrEarpiece(route, mCallAudioState.getSupportedRouteMask());
    128 
    129         // If route is unsupported, do nothing.
    130         if ((mCallAudioState.getSupportedRouteMask() | newRoute) == 0) {
    131             Log.w(TAG, "Asking to set to a route that is unsupported: " + newRoute);
    132             return;
    133         }
    134 
    135         if (mCallAudioState.getRoute() != newRoute) {
    136             // Remember the new speaker state so it can be restored when the user plugs and unplugs
    137             // a headset.
    138             mWasSpeakerOn = newRoute == CallAudioState.ROUTE_SPEAKER;
    139             setSystemAudioState(new CallAudioState(false /* muted */, newRoute,
    140                     mCallAudioState.getSupportedRouteMask()));
    141         }
    142     }
    143 
    144     private CallAudioState getInitialAudioState() {
    145         int supportedRouteMask = calculateSupportedRoutes();
    146         int route = selectWiredOrEarpiece(CallAudioState.ROUTE_WIRED_OR_EARPIECE,
    147                 supportedRouteMask);
    148         return new CallAudioState(false /* muted */, route, supportedRouteMask);
    149     }
    150 
    151     private int calculateSupportedRoutes() {
    152         int routeMask = CallAudioState.ROUTE_SPEAKER;
    153         if (mWiredHeadsetManager.isPluggedIn()) {
    154             routeMask |= CallAudioState.ROUTE_WIRED_HEADSET;
    155         } else {
    156             routeMask |= CallAudioState.ROUTE_EARPIECE;
    157         }
    158         return routeMask;
    159     }
    160 
    161     private int selectWiredOrEarpiece(int route, int supportedRouteMask) {
    162         // Since they are mutually exclusive and one is ALWAYS valid, we allow a special input of
    163         // ROUTE_WIRED_OR_EARPIECE so that callers don't have to make a call to check which is
    164         // supported before calling setAudioRoute.
    165         if (route == CallAudioState.ROUTE_WIRED_OR_EARPIECE) {
    166             route = CallAudioState.ROUTE_WIRED_OR_EARPIECE & supportedRouteMask;
    167             if (route == 0) {
    168                 Log.wtf(TAG, "One of wired headset or earpiece should always be valid.");
    169                 // assume earpiece in this case.
    170                 route = CallAudioState.ROUTE_EARPIECE;
    171             }
    172         }
    173         return route;
    174     }
    175 
    176     private void setSystemAudioState(CallAudioState callAudioState) {
    177         CallAudioState oldAudioState = mCallAudioState;
    178         mCallAudioState = callAudioState;
    179 
    180         Log.i(TAG, "setSystemAudioState: changing from " + oldAudioState + " to "
    181                 + mCallAudioState);
    182 
    183         // Audio route.
    184         if (mCallAudioState.getRoute() == CallAudioState.ROUTE_SPEAKER) {
    185             turnOnSpeaker(true);
    186         } else if (mCallAudioState.getRoute() == CallAudioState.ROUTE_EARPIECE ||
    187                 mCallAudioState.getRoute() == CallAudioState.ROUTE_WIRED_HEADSET) {
    188             // Just handle turning off the speaker, the system will handle switching between wired
    189             // headset and earpiece.
    190             turnOnSpeaker(false);
    191         }
    192     }
    193 
    194     private void turnOnSpeaker(boolean on) {
    195         if (mAudioManager.isSpeakerphoneOn() != on) {
    196             Log.i(TAG, "turning speaker phone on: " + on);
    197             mAudioManager.setSpeakerphoneOn(on);
    198         }
    199     }
    200 }
    201