• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
VoicemailAudioManager(Context context, VoicemailPlaybackPresenter voicemailPlaybackPresenter)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 
requestAudioFocus()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 
abandonAudioFocus()63     public void abandonAudioFocus() {
64         mAudioManager.abandonAudioFocus(this);
65     }
66 
67     @Override
onAudioFocusChange(int focusChange)68     public void onAudioFocusChange(int focusChange) {
69         Log.d(TAG, "onAudioFocusChange: focusChange=" + focusChange);
70         mVoicemailPlaybackPresenter.onAudioFocusChange(focusChange == AudioManager.AUDIOFOCUS_GAIN);
71     }
72 
73     @Override
onWiredHeadsetPluggedInChanged(boolean oldIsPluggedIn, boolean newIsPluggedIn)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 
setSpeakerphoneOn(boolean on)101     public void setSpeakerphoneOn(boolean on) {
102         setAudioRoute(on ? CallAudioState.ROUTE_SPEAKER : CallAudioState.ROUTE_WIRED_OR_EARPIECE);
103     }
104 
isWiredHeadsetPluggedIn()105     public boolean isWiredHeadsetPluggedIn() {
106         return mWiredHeadsetManager.isPluggedIn();
107     }
108 
registerReceivers()109     public void registerReceivers() {
110         // Receivers is plural because we expect to add bluetooth support.
111         mWiredHeadsetManager.registerReceiver();
112     }
113 
unregisterReceivers()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      */
setAudioRoute(int route)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 
getInitialAudioState()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 
calculateSupportedRoutes()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 
selectWiredOrEarpiece(int route, int supportedRouteMask)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 
setSystemAudioState(CallAudioState callAudioState)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 
turnOnSpeaker(boolean on)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