• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 com.android.server.telecom;
18 
19 import android.app.ActivityManagerNative;
20 import android.content.Context;
21 import android.content.pm.UserInfo;
22 import android.media.AudioManager;
23 import android.media.IAudioService;
24 import android.os.Binder;
25 import android.os.Handler;
26 import android.os.IBinder;
27 import android.os.Looper;
28 import android.os.Message;
29 import android.os.RemoteException;
30 import android.os.ServiceManager;
31 import android.os.UserHandle;
32 import android.telecom.CallAudioState;
33 
34 import com.android.internal.util.IndentingPrintWriter;
35 import com.android.internal.util.Preconditions;
36 
37 import java.util.Objects;
38 
39 /**
40  * This class manages audio modes, streams and other properties.
41  */
42 final class CallAudioManager extends CallsManagerListenerBase
43         implements WiredHeadsetManager.Listener, DockManager.Listener {
44     private static final int STREAM_NONE = -1;
45 
46     private static final String STREAM_DESCRIPTION_NONE = "STEAM_NONE";
47     private static final String STREAM_DESCRIPTION_ALARM = "STEAM_ALARM";
48     private static final String STREAM_DESCRIPTION_BLUETOOTH_SCO = "STREAM_BLUETOOTH_SCO";
49     private static final String STREAM_DESCRIPTION_DTMF = "STREAM_DTMF";
50     private static final String STREAM_DESCRIPTION_MUSIC = "STREAM_MUSIC";
51     private static final String STREAM_DESCRIPTION_NOTIFICATION = "STREAM_NOTIFICATION";
52     private static final String STREAM_DESCRIPTION_RING = "STREAM_RING";
53     private static final String STREAM_DESCRIPTION_SYSTEM = "STREAM_SYSTEM";
54     private static final String STREAM_DESCRIPTION_VOICE_CALL = "STREAM_VOICE_CALL";
55 
56     private static final String MODE_DESCRIPTION_INVALID = "MODE_INVALID";
57     private static final String MODE_DESCRIPTION_CURRENT = "MODE_CURRENT";
58     private static final String MODE_DESCRIPTION_NORMAL = "MODE_NORMAL";
59     private static final String MODE_DESCRIPTION_RINGTONE = "MODE_RINGTONE";
60     private static final String MODE_DESCRIPTION_IN_CALL = "MODE_IN_CALL";
61     private static final String MODE_DESCRIPTION_IN_COMMUNICATION = "MODE_IN_COMMUNICATION";
62 
63     private static final int MSG_AUDIO_MANAGER_INITIALIZE = 0;
64     private static final int MSG_AUDIO_MANAGER_TURN_ON_SPEAKER = 1;
65     private static final int MSG_AUDIO_MANAGER_ABANDON_AUDIO_FOCUS_FOR_CALL = 2;
66     private static final int MSG_AUDIO_MANAGER_SET_MICROPHONE_MUTE = 3;
67     private static final int MSG_AUDIO_MANAGER_REQUEST_AUDIO_FOCUS_FOR_CALL = 4;
68     private static final int MSG_AUDIO_MANAGER_SET_MODE = 5;
69 
70     private final Handler mAudioManagerHandler = new Handler(Looper.getMainLooper()) {
71 
72         private AudioManager mAudioManager;
73 
74         @Override
75         public void handleMessage(Message msg) {
76             switch (msg.what) {
77                 case MSG_AUDIO_MANAGER_INITIALIZE: {
78                     mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
79                     break;
80                 }
81                 case MSG_AUDIO_MANAGER_TURN_ON_SPEAKER: {
82                     boolean on = (msg.arg1 != 0);
83                     // Wired headset and earpiece work the same way
84                     if (mAudioManager.isSpeakerphoneOn() != on) {
85                         Log.i(this, "turning speaker phone %s", on);
86                         mAudioManager.setSpeakerphoneOn(on);
87                     }
88                     break;
89                 }
90                 case MSG_AUDIO_MANAGER_ABANDON_AUDIO_FOCUS_FOR_CALL: {
91                     mAudioManager.abandonAudioFocusForCall();
92                     break;
93                 }
94                 case MSG_AUDIO_MANAGER_SET_MICROPHONE_MUTE: {
95                     boolean mute = (msg.arg1 != 0);
96                     if (mute != mAudioManager.isMicrophoneMute()) {
97                         IAudioService audio = getAudioService();
98                         Log.i(this, "changing microphone mute state to: %b [serviceIsNull=%b]",
99                                 mute, audio == null);
100                         if (audio != null) {
101                             try {
102                                 // We use the audio service directly here so that we can specify
103                                 // the current user. Telecom runs in the system_server process which
104                                 // may run as a separate user from the foreground user. If we
105                                 // used AudioManager directly, we would change mute for the system's
106                                 // user and not the current foreground, which we want to avoid.
107                                 audio.setMicrophoneMute(
108                                         mute, mContext.getOpPackageName(), getCurrentUserId());
109 
110                             } catch (RemoteException e) {
111                                 Log.e(this, e, "Remote exception while toggling mute.");
112                             }
113                             // TODO: Check microphone state after attempting to set to ensure that
114                             // our state corroborates AudioManager's state.
115                         }
116                     }
117 
118                     break;
119                 }
120                 case MSG_AUDIO_MANAGER_REQUEST_AUDIO_FOCUS_FOR_CALL: {
121                     int stream = msg.arg1;
122                     mAudioManager.requestAudioFocusForCall(
123                             stream,
124                             AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
125                     break;
126                 }
127                 case MSG_AUDIO_MANAGER_SET_MODE: {
128                     int newMode = msg.arg1;
129                     int oldMode = mAudioManager.getMode();
130                     Log.v(this, "Request to change audio mode from %s to %s", modeToString(oldMode),
131                             modeToString(newMode));
132 
133                     if (oldMode != newMode) {
134                         if (oldMode == AudioManager.MODE_IN_CALL &&
135                                 newMode == AudioManager.MODE_RINGTONE) {
136                             Log.i(this, "Transition from IN_CALL -> RINGTONE."
137                                     + "  Resetting to NORMAL first.");
138                             mAudioManager.setMode(AudioManager.MODE_NORMAL);
139                         }
140                         mAudioManager.setMode(newMode);
141                         synchronized (mLock) {
142                             mMostRecentlyUsedMode = newMode;
143                         }
144                     }
145                     break;
146                 }
147                 default:
148                     break;
149             }
150         }
151     };
152 
153     private final Context mContext;
154     private final TelecomSystem.SyncRoot mLock;
155     private final StatusBarNotifier mStatusBarNotifier;
156     private final BluetoothManager mBluetoothManager;
157     private final WiredHeadsetManager mWiredHeadsetManager;
158     private final DockManager mDockManager;
159     private final CallsManager mCallsManager;
160 
161     private CallAudioState mCallAudioState;
162     private int mAudioFocusStreamType;
163     private boolean mIsRinging;
164     private boolean mIsTonePlaying;
165     private boolean mWasSpeakerOn;
166     private int mMostRecentlyUsedMode = AudioManager.MODE_IN_CALL;
167     private Call mCallToSpeedUpMTAudio = null;
168 
CallAudioManager( Context context, TelecomSystem.SyncRoot lock, StatusBarNotifier statusBarNotifier, WiredHeadsetManager wiredHeadsetManager, DockManager dockManager, CallsManager callsManager)169     CallAudioManager(
170             Context context,
171             TelecomSystem.SyncRoot lock,
172             StatusBarNotifier statusBarNotifier,
173             WiredHeadsetManager wiredHeadsetManager,
174             DockManager dockManager,
175             CallsManager callsManager) {
176         mContext = context;
177         mLock = lock;
178         mAudioManagerHandler.obtainMessage(MSG_AUDIO_MANAGER_INITIALIZE, 0, 0).sendToTarget();
179         mStatusBarNotifier = statusBarNotifier;
180         mBluetoothManager = new BluetoothManager(context, this);
181         mWiredHeadsetManager = wiredHeadsetManager;
182         mCallsManager = callsManager;
183 
184         mWiredHeadsetManager.addListener(this);
185         mDockManager = dockManager;
186         mDockManager.addListener(this);
187 
188         saveAudioState(getInitialAudioState(null));
189         mAudioFocusStreamType = STREAM_NONE;
190     }
191 
getCallAudioState()192     CallAudioState getCallAudioState() {
193         return mCallAudioState;
194     }
195 
196     @Override
onCallAdded(Call call)197     public void onCallAdded(Call call) {
198         Log.v(this, "onCallAdded");
199         onCallUpdated(call);
200 
201         if (hasFocus() && getForegroundCall() == call) {
202             if (!call.isIncoming()) {
203                 // Unmute new outgoing call.
204                 setSystemAudioState(false, mCallAudioState.getRoute(),
205                         mCallAudioState.getSupportedRouteMask());
206             }
207         }
208     }
209 
210     @Override
onCallRemoved(Call call)211     public void onCallRemoved(Call call) {
212         Log.v(this, "onCallRemoved");
213         // If we didn't already have focus, there's nothing to do.
214         if (hasFocus()) {
215             if (mCallsManager.getCalls().isEmpty()) {
216                 Log.v(this, "all calls removed, resetting system audio to default state");
217                 setInitialAudioState(null, false /* force */);
218                 mWasSpeakerOn = false;
219             }
220             updateAudioStreamAndMode(call);
221         }
222     }
223 
224     @Override
onCallStateChanged(Call call, int oldState, int newState)225     public void onCallStateChanged(Call call, int oldState, int newState) {
226         Log.v(this, "onCallStateChanged : oldState = %d, newState = %d", oldState, newState);
227         onCallUpdated(call);
228     }
229 
230     @Override
onIncomingCallAnswered(Call call)231     public void onIncomingCallAnswered(Call call) {
232         Log.v(this, "onIncomingCallAnswered");
233         int route = mCallAudioState.getRoute();
234 
235         // We do two things:
236         // (1) If this is the first call, then we can to turn on bluetooth if available.
237         // (2) Unmute the audio for the new incoming call.
238         boolean isOnlyCall = mCallsManager.getCalls().size() == 1;
239         if (isOnlyCall && mBluetoothManager.isBluetoothAvailable()) {
240             mBluetoothManager.connectBluetoothAudio();
241             route = CallAudioState.ROUTE_BLUETOOTH;
242         }
243 
244         setSystemAudioState(false /* isMute */, route, mCallAudioState.getSupportedRouteMask());
245 
246         if (call.can(android.telecom.Call.Details.CAPABILITY_SPEED_UP_MT_AUDIO)) {
247             Log.v(this, "Speed up audio setup for IMS MT call.");
248             mCallToSpeedUpMTAudio = call;
249             updateAudioStreamAndMode(call);
250         }
251     }
252 
253     @Override
onForegroundCallChanged(Call oldForegroundCall, Call newForegroundCall)254     public void onForegroundCallChanged(Call oldForegroundCall, Call newForegroundCall) {
255         onCallUpdated(newForegroundCall);
256         // Ensure that the foreground call knows about the latest audio state.
257         updateAudioForForegroundCall();
258     }
259 
260     @Override
onIsVoipAudioModeChanged(Call call)261     public void onIsVoipAudioModeChanged(Call call) {
262         updateAudioStreamAndMode(call);
263     }
264 
265     /**
266       * Updates the audio route when the headset plugged in state changes. For example, if audio is
267       * being routed over speakerphone and a headset is plugged in then switch to wired headset.
268       */
269     @Override
onWiredHeadsetPluggedInChanged(boolean oldIsPluggedIn, boolean newIsPluggedIn)270     public void onWiredHeadsetPluggedInChanged(boolean oldIsPluggedIn, boolean newIsPluggedIn) {
271         // This can happen even when there are no calls and we don't have focus.
272         if (!hasFocus()) {
273             return;
274         }
275 
276         boolean isCurrentlyWiredHeadset = mCallAudioState.getRoute()
277                 == CallAudioState.ROUTE_WIRED_HEADSET;
278 
279         int newRoute = mCallAudioState.getRoute();  // start out with existing route
280         if (newIsPluggedIn) {
281             newRoute = CallAudioState.ROUTE_WIRED_HEADSET;
282         } else if (isCurrentlyWiredHeadset) {
283             Call call = getForegroundCall();
284             boolean hasLiveCall = call != null && call.isAlive();
285 
286             if (hasLiveCall) {
287                 // In order of preference when a wireless headset is unplugged.
288                 if (mWasSpeakerOn) {
289                     newRoute = CallAudioState.ROUTE_SPEAKER;
290                 } else {
291                     newRoute = CallAudioState.ROUTE_EARPIECE;
292                 }
293 
294                 // We don't automatically connect to bluetooth when user unplugs their wired headset
295                 // and they were previously using the wired. Wired and earpiece are effectively the
296                 // same choice in that they replace each other as an option when wired headsets
297                 // are plugged in and out. This means that keeping it earpiece is a bit more
298                 // consistent with the status quo.  Bluetooth also has more danger associated with
299                 // choosing it in the wrong curcumstance because bluetooth devices can be
300                 // semi-public (like in a very-occupied car) where earpiece doesn't carry that risk.
301             }
302         }
303 
304         // We need to call this every time even if we do not change the route because the supported
305         // routes changed either to include or not include WIRED_HEADSET.
306         setSystemAudioState(mCallAudioState.isMuted(), newRoute, calculateSupportedRoutes());
307     }
308 
309     @Override
onDockChanged(boolean isDocked)310     public void onDockChanged(boolean isDocked) {
311         // This can happen even when there are no calls and we don't have focus.
312         if (!hasFocus()) {
313             return;
314         }
315 
316         if (isDocked) {
317             // Device just docked, turn to speakerphone. Only do so if the route is currently
318             // earpiece so that we dont switch out of a BT headset or a wired headset.
319             if (mCallAudioState.getRoute() == CallAudioState.ROUTE_EARPIECE) {
320                 setAudioRoute(CallAudioState.ROUTE_SPEAKER);
321             }
322         } else {
323             // Device just undocked, remove from speakerphone if possible.
324             if (mCallAudioState.getRoute() == CallAudioState.ROUTE_SPEAKER) {
325                 setAudioRoute(CallAudioState.ROUTE_WIRED_OR_EARPIECE);
326             }
327         }
328     }
329 
toggleMute()330     void toggleMute() {
331         mute(!mCallAudioState.isMuted());
332     }
333 
mute(boolean shouldMute)334     void mute(boolean shouldMute) {
335         if (!hasFocus()) {
336             return;
337         }
338 
339         Log.v(this, "mute, shouldMute: %b", shouldMute);
340 
341         // Don't mute if there are any emergency calls.
342         if (mCallsManager.hasEmergencyCall()) {
343             shouldMute = false;
344             Log.v(this, "ignoring mute for emergency call");
345         }
346 
347         if (mCallAudioState.isMuted() != shouldMute) {
348             // We user CallsManager's foreground call so that we dont ignore ringing calls
349             // for logging purposes
350             Log.event(mCallsManager.getForegroundCall(), Log.Events.MUTE,
351                     shouldMute ? "on" : "off");
352 
353             setSystemAudioState(shouldMute, mCallAudioState.getRoute(),
354                     mCallAudioState.getSupportedRouteMask());
355         }
356     }
357 
358     /**
359      * Changed the audio route, for example from earpiece to speaker phone.
360      *
361      * @param route The new audio route to use. See {@link CallAudioState}.
362      */
setAudioRoute(int route)363     void setAudioRoute(int route) {
364         // This can happen even when there are no calls and we don't have focus.
365         if (!hasFocus()) {
366             return;
367         }
368 
369         Log.v(this, "setAudioRoute, route: %s", CallAudioState.audioRouteToString(route));
370 
371         // Change ROUTE_WIRED_OR_EARPIECE to a single entry.
372         int newRoute = selectWiredOrEarpiece(route, mCallAudioState.getSupportedRouteMask());
373 
374         // If route is unsupported, do nothing.
375         if ((mCallAudioState.getSupportedRouteMask() | newRoute) == 0) {
376             Log.wtf(this, "Asking to set to a route that is unsupported: %d", newRoute);
377             return;
378         }
379 
380         if (mCallAudioState.getRoute() != newRoute) {
381             // Remember the new speaker state so it can be restored when the user plugs and unplugs
382             // a headset.
383             mWasSpeakerOn = newRoute == CallAudioState.ROUTE_SPEAKER;
384             setSystemAudioState(mCallAudioState.isMuted(), newRoute,
385                     mCallAudioState.getSupportedRouteMask());
386         }
387     }
388 
389     /**
390      * Sets the audio stream and mode based on whether a call is ringing.
391      *
392      * @param call The call which changed ringing state.
393      * @param isRinging {@code true} if the call is ringing, {@code false} otherwise.
394      */
setIsRinging(Call call, boolean isRinging)395     void setIsRinging(Call call, boolean isRinging) {
396         if (mIsRinging != isRinging) {
397             Log.i(this, "setIsRinging %b -> %b (call = %s)", mIsRinging, isRinging, call);
398             mIsRinging = isRinging;
399             updateAudioStreamAndMode(call);
400         }
401     }
402 
403     /**
404      * Sets the tone playing status. Some tones can play even when there are no live calls and this
405      * status indicates that we should keep audio focus even for tones that play beyond the life of
406      * calls.
407      *
408      * @param isPlayingNew The status to set.
409      */
setIsTonePlaying(boolean isPlayingNew)410     void setIsTonePlaying(boolean isPlayingNew) {
411         if (mIsTonePlaying != isPlayingNew) {
412             Log.v(this, "mIsTonePlaying %b -> %b.", mIsTonePlaying, isPlayingNew);
413             mIsTonePlaying = isPlayingNew;
414             updateAudioStreamAndMode();
415         }
416     }
417 
418     /**
419      * Updates the audio routing according to the bluetooth state.
420      */
onBluetoothStateChange(BluetoothManager bluetoothManager)421     void onBluetoothStateChange(BluetoothManager bluetoothManager) {
422         // This can happen even when there are no calls and we don't have focus.
423         if (!hasFocus()) {
424             return;
425         }
426 
427         int supportedRoutes = calculateSupportedRoutes();
428         int newRoute = mCallAudioState.getRoute();
429         if (bluetoothManager.isBluetoothAudioConnectedOrPending()) {
430             newRoute = CallAudioState.ROUTE_BLUETOOTH;
431         } else if (mCallAudioState.getRoute() == CallAudioState.ROUTE_BLUETOOTH) {
432             newRoute = selectWiredOrEarpiece(CallAudioState.ROUTE_WIRED_OR_EARPIECE,
433                     supportedRoutes);
434             // Do not switch to speaker when bluetooth disconnects.
435             mWasSpeakerOn = false;
436         }
437 
438         setSystemAudioState(mCallAudioState.isMuted(), newRoute, supportedRoutes);
439     }
440 
isBluetoothAudioOn()441     boolean isBluetoothAudioOn() {
442         return mBluetoothManager.isBluetoothAudioConnected();
443     }
444 
isBluetoothDeviceAvailable()445     boolean isBluetoothDeviceAvailable() {
446         return mBluetoothManager.isBluetoothAvailable();
447     }
448 
saveAudioState(CallAudioState callAudioState)449     private void saveAudioState(CallAudioState callAudioState) {
450         mCallAudioState = callAudioState;
451         mStatusBarNotifier.notifyMute(mCallAudioState.isMuted());
452         mStatusBarNotifier.notifySpeakerphone(mCallAudioState.getRoute()
453                 == CallAudioState.ROUTE_SPEAKER);
454     }
455 
onCallUpdated(Call call)456     private void onCallUpdated(Call call) {
457         updateAudioStreamAndMode(call);
458         if (call != null && call.getState() == CallState.ACTIVE &&
459                             call == mCallToSpeedUpMTAudio) {
460             mCallToSpeedUpMTAudio = null;
461         }
462     }
463 
setSystemAudioState(boolean isMuted, int route, int supportedRouteMask)464     private void setSystemAudioState(boolean isMuted, int route, int supportedRouteMask) {
465         setSystemAudioState(false /* force */, isMuted, route, supportedRouteMask);
466     }
467 
setSystemAudioState( boolean force, boolean isMuted, int route, int supportedRouteMask)468     private void setSystemAudioState(
469             boolean force, boolean isMuted, int route, int supportedRouteMask) {
470         if (!hasFocus()) {
471             return;
472         }
473 
474         CallAudioState oldAudioState = mCallAudioState;
475         saveAudioState(new CallAudioState(isMuted, route, supportedRouteMask));
476         if (!force && Objects.equals(oldAudioState, mCallAudioState)) {
477             return;
478         }
479 
480         Log.i(this, "setSystemAudioState: changing from %s to %s", oldAudioState, mCallAudioState);
481         Log.event(mCallsManager.getForegroundCall(), Log.Events.AUDIO_ROUTE,
482                 CallAudioState.audioRouteToString(mCallAudioState.getRoute()));
483 
484         mAudioManagerHandler.obtainMessage(
485                 MSG_AUDIO_MANAGER_SET_MICROPHONE_MUTE,
486                 mCallAudioState.isMuted() ? 1 : 0,
487                 0)
488                 .sendToTarget();
489 
490         // Audio route.
491         if (mCallAudioState.getRoute() == CallAudioState.ROUTE_BLUETOOTH) {
492             turnOnSpeaker(false);
493             turnOnBluetooth(true);
494         } else if (mCallAudioState.getRoute() == CallAudioState.ROUTE_SPEAKER) {
495             turnOnBluetooth(false);
496             turnOnSpeaker(true);
497         } else if (mCallAudioState.getRoute() == CallAudioState.ROUTE_EARPIECE ||
498                 mCallAudioState.getRoute() == CallAudioState.ROUTE_WIRED_HEADSET) {
499             turnOnBluetooth(false);
500             turnOnSpeaker(false);
501         }
502 
503         if (!oldAudioState.equals(mCallAudioState)) {
504             mCallsManager.onCallAudioStateChanged(oldAudioState, mCallAudioState);
505             updateAudioForForegroundCall();
506         }
507     }
508 
turnOnSpeaker(boolean on)509     private void turnOnSpeaker(boolean on) {
510         mAudioManagerHandler.obtainMessage(MSG_AUDIO_MANAGER_TURN_ON_SPEAKER, on ? 1 : 0, 0)
511                 .sendToTarget();
512     }
513 
turnOnBluetooth(boolean on)514     private void turnOnBluetooth(boolean on) {
515         if (mBluetoothManager.isBluetoothAvailable()) {
516             boolean isAlreadyOn = mBluetoothManager.isBluetoothAudioConnectedOrPending();
517             if (on != isAlreadyOn) {
518                 Log.i(this, "connecting bluetooth %s", on);
519                 if (on) {
520                     mBluetoothManager.connectBluetoothAudio();
521                 } else {
522                     mBluetoothManager.disconnectBluetoothAudio();
523                 }
524             }
525         }
526     }
527 
updateAudioStreamAndMode()528     private void updateAudioStreamAndMode() {
529         updateAudioStreamAndMode(null /* call */);
530     }
531 
updateAudioStreamAndMode(Call callToUpdate)532     private void updateAudioStreamAndMode(Call callToUpdate) {
533         Log.i(this, "updateAudioStreamAndMode :  mIsRinging: %b, mIsTonePlaying: %b, call: %s",
534                 mIsRinging, mIsTonePlaying, callToUpdate);
535 
536         boolean wasVoiceCall = mAudioFocusStreamType == AudioManager.STREAM_VOICE_CALL;
537         if (mIsRinging) {
538             Log.i(this, "updateAudioStreamAndMode : ringing");
539             requestAudioFocusAndSetMode(AudioManager.STREAM_RING, AudioManager.MODE_RINGTONE);
540         } else {
541             Call foregroundCall = getForegroundCall();
542             Call waitingForAccountSelectionCall = mCallsManager
543                     .getFirstCallWithState(CallState.SELECT_PHONE_ACCOUNT);
544             Call call = mCallsManager.getForegroundCall();
545             if (foregroundCall == null && call != null && call == mCallToSpeedUpMTAudio) {
546                 Log.v(this, "updateAudioStreamAndMode : no foreground, speeding up MT audio.");
547                 requestAudioFocusAndSetMode(AudioManager.STREAM_VOICE_CALL,
548                                                          AudioManager.MODE_IN_CALL);
549             } else if (foregroundCall != null && !foregroundCall.isDisconnected() &&
550                     waitingForAccountSelectionCall == null) {
551                 // In the case where there is a call that is waiting for account selection,
552                 // this will fall back to abandonAudioFocus() below, which temporarily exits
553                 // the in-call audio mode. This is to allow TalkBack to speak the "Call with"
554                 // dialog information at media volume as opposed to through the earpiece.
555                 // Once exiting the "Call with" dialog, the audio focus will return to an in-call
556                 // audio mode when this method (updateAudioStreamAndMode) is called again.
557                 int mode = foregroundCall.getIsVoipAudioMode() ?
558                         AudioManager.MODE_IN_COMMUNICATION : AudioManager.MODE_IN_CALL;
559                 Log.v(this, "updateAudioStreamAndMode : foreground");
560                 requestAudioFocusAndSetMode(AudioManager.STREAM_VOICE_CALL, mode);
561             } else if (mIsTonePlaying) {
562                 // There is no call, however, we are still playing a tone, so keep focus.
563                 // Since there is no call from which to determine the mode, use the most
564                 // recently used mode instead.
565                 Log.v(this, "updateAudioStreamAndMode : tone playing");
566                 requestAudioFocusAndSetMode(
567                         AudioManager.STREAM_VOICE_CALL, mMostRecentlyUsedMode);
568             } else if (!hasRingingForegroundCall() && mCallsManager.hasOnlyDisconnectedCalls()) {
569                 Log.v(this, "updateAudioStreamAndMode : no ringing call");
570                 abandonAudioFocus();
571             } else {
572                 // mIsRinging is false, but there is a foreground ringing call present. Don't
573                 // abandon audio focus immediately to prevent audio focus from getting lost between
574                 // the time it takes for the foreground call to transition from RINGING to ACTIVE/
575                 // DISCONNECTED. When the call eventually transitions to the next state, audio
576                 // focus will be correctly abandoned by the if clause above.
577             }
578         }
579 
580         boolean isVoiceCall = mAudioFocusStreamType == AudioManager.STREAM_VOICE_CALL;
581 
582         // If we transition from not a voice call to a voice call, we need to set an initial audio
583         // state for the call.
584         if (!wasVoiceCall && isVoiceCall) {
585             setInitialAudioState(callToUpdate, true /* force */);
586         }
587     }
588 
requestAudioFocusAndSetMode(int stream, int mode)589     private void requestAudioFocusAndSetMode(int stream, int mode) {
590         Log.v(this, "requestAudioFocusAndSetMode : stream: %s -> %s, mode: %s",
591                 streamTypeToString(mAudioFocusStreamType), streamTypeToString(stream),
592                 modeToString(mode));
593         Preconditions.checkState(stream != STREAM_NONE);
594 
595         // Even if we already have focus, if the stream is different we update audio manager to give
596         // it a hint about the purpose of our focus.
597         if (mAudioFocusStreamType != stream) {
598             Log.i(this, "requestAudioFocusAndSetMode : requesting stream: %s -> %s",
599                     streamTypeToString(mAudioFocusStreamType), streamTypeToString(stream));
600             mAudioManagerHandler.obtainMessage(
601                     MSG_AUDIO_MANAGER_REQUEST_AUDIO_FOCUS_FOR_CALL,
602                     stream,
603                     0)
604                     .sendToTarget();
605         }
606         mAudioFocusStreamType = stream;
607 
608         setMode(mode);
609     }
610 
abandonAudioFocus()611     private void abandonAudioFocus() {
612         if (hasFocus()) {
613             setMode(AudioManager.MODE_NORMAL);
614             Log.v(this, "abandoning audio focus");
615             mAudioManagerHandler.obtainMessage(MSG_AUDIO_MANAGER_ABANDON_AUDIO_FOCUS_FOR_CALL, 0, 0)
616                     .sendToTarget();
617             mAudioFocusStreamType = STREAM_NONE;
618             mCallToSpeedUpMTAudio = null;
619         }
620     }
621 
622     /**
623      * Sets the audio mode.
624      *
625      * @param newMode Mode constant from AudioManager.MODE_*.
626      */
setMode(int newMode)627     private void setMode(int newMode) {
628         Preconditions.checkState(hasFocus());
629         mAudioManagerHandler.obtainMessage(MSG_AUDIO_MANAGER_SET_MODE, newMode, 0).sendToTarget();
630     }
631 
selectWiredOrEarpiece(int route, int supportedRouteMask)632     private int selectWiredOrEarpiece(int route, int supportedRouteMask) {
633         // Since they are mutually exclusive and one is ALWAYS valid, we allow a special input of
634         // ROUTE_WIRED_OR_EARPIECE so that callers dont have to make a call to check which is
635         // supported before calling setAudioRoute.
636         if (route == CallAudioState.ROUTE_WIRED_OR_EARPIECE) {
637             route = CallAudioState.ROUTE_WIRED_OR_EARPIECE & supportedRouteMask;
638             if (route == 0) {
639                 Log.wtf(this, "One of wired headset or earpiece should always be valid.");
640                 // assume earpiece in this case.
641                 route = CallAudioState.ROUTE_EARPIECE;
642             }
643         }
644         return route;
645     }
646 
calculateSupportedRoutes()647     private int calculateSupportedRoutes() {
648         int routeMask = CallAudioState.ROUTE_SPEAKER;
649 
650         if (mWiredHeadsetManager.isPluggedIn()) {
651             routeMask |= CallAudioState.ROUTE_WIRED_HEADSET;
652         } else {
653             routeMask |= CallAudioState.ROUTE_EARPIECE;
654         }
655 
656         if (mBluetoothManager.isBluetoothAvailable()) {
657             routeMask |=  CallAudioState.ROUTE_BLUETOOTH;
658         }
659 
660         return routeMask;
661     }
662 
getInitialAudioState(Call call)663     private CallAudioState getInitialAudioState(Call call) {
664         int supportedRouteMask = calculateSupportedRoutes();
665         int route = selectWiredOrEarpiece(
666                 CallAudioState.ROUTE_WIRED_OR_EARPIECE, supportedRouteMask);
667 
668         // We want the UI to indicate that "bluetooth is in use" in two slightly different cases:
669         // (a) The obvious case: if a bluetooth headset is currently in use for an ongoing call.
670         // (b) The not-so-obvious case: if an incoming call is ringing, and we expect that audio
671         //     *will* be routed to a bluetooth headset once the call is answered. In this case, just
672         //     check if the headset is available. Note this only applies when we are dealing with
673         //     the first call.
674         if (call != null && mBluetoothManager.isBluetoothAvailable()) {
675             switch(call.getState()) {
676                 case CallState.ACTIVE:
677                 case CallState.ON_HOLD:
678                 case CallState.DIALING:
679                 case CallState.CONNECTING:
680                 case CallState.RINGING:
681                     route = CallAudioState.ROUTE_BLUETOOTH;
682                     break;
683                 default:
684                     break;
685             }
686         }
687 
688         return new CallAudioState(false, route, supportedRouteMask);
689     }
690 
setInitialAudioState(Call call, boolean force)691     private void setInitialAudioState(Call call, boolean force) {
692         CallAudioState audioState = getInitialAudioState(call);
693         Log.i(this, "setInitialAudioState : audioState = %s, call = %s", audioState, call);
694         setSystemAudioState(
695                 force, audioState.isMuted(), audioState.getRoute(),
696                 audioState.getSupportedRouteMask());
697     }
698 
updateAudioForForegroundCall()699     private void updateAudioForForegroundCall() {
700         Call call = mCallsManager.getForegroundCall();
701         if (call != null && call.getConnectionService() != null) {
702             call.getConnectionService().onCallAudioStateChanged(call, mCallAudioState);
703         }
704     }
705 
706     /**
707      * Returns the current foreground call in order to properly set the audio mode.
708      */
getForegroundCall()709     private Call getForegroundCall() {
710         Call call = mCallsManager.getForegroundCall();
711 
712         // We ignore any foreground call that is in the ringing state because we deal with ringing
713         // calls exclusively through the mIsRinging variable set by {@link Ringer}.
714         if (call != null && call.getState() == CallState.RINGING) {
715             return null;
716         }
717 
718         return call;
719     }
720 
hasRingingForegroundCall()721     private boolean hasRingingForegroundCall() {
722         Call call = mCallsManager.getForegroundCall();
723         return call != null && call.getState() == CallState.RINGING;
724     }
725 
hasFocus()726     private boolean hasFocus() {
727         return mAudioFocusStreamType != STREAM_NONE;
728     }
729 
getAudioService()730     private IAudioService getAudioService() {
731         return IAudioService.Stub.asInterface(ServiceManager.getService(Context.AUDIO_SERVICE));
732     }
733 
getCurrentUserId()734     private int getCurrentUserId() {
735         final long ident = Binder.clearCallingIdentity();
736         try {
737             UserInfo currentUser = ActivityManagerNative.getDefault().getCurrentUser();
738             return currentUser.id;
739         } catch (RemoteException e) {
740             // Activity manager not running, nothing we can do assume user 0.
741         } finally {
742             Binder.restoreCallingIdentity(ident);
743         }
744         return UserHandle.USER_OWNER;
745     }
746 
747     /**
748      * Translates an {@link AudioManager} stream type to a human-readable string description.
749      *
750      * @param streamType The stream type.
751      * @return Human readable description.
752      */
streamTypeToString(int streamType)753     private String streamTypeToString(int streamType) {
754         switch (streamType) {
755             case STREAM_NONE:
756                 return STREAM_DESCRIPTION_NONE;
757             case AudioManager.STREAM_ALARM:
758                 return STREAM_DESCRIPTION_ALARM;
759             case AudioManager.STREAM_BLUETOOTH_SCO:
760                 return STREAM_DESCRIPTION_BLUETOOTH_SCO;
761             case AudioManager.STREAM_DTMF:
762                 return STREAM_DESCRIPTION_DTMF;
763             case AudioManager.STREAM_MUSIC:
764                 return STREAM_DESCRIPTION_MUSIC;
765             case AudioManager.STREAM_NOTIFICATION:
766                 return STREAM_DESCRIPTION_NOTIFICATION;
767             case AudioManager.STREAM_RING:
768                 return STREAM_DESCRIPTION_RING;
769             case AudioManager.STREAM_SYSTEM:
770                 return STREAM_DESCRIPTION_SYSTEM;
771             case AudioManager.STREAM_VOICE_CALL:
772                 return STREAM_DESCRIPTION_VOICE_CALL;
773             default:
774                 return "STEAM_OTHER_" + streamType;
775         }
776     }
777 
778     /**
779      * Translates an {@link AudioManager} mode into a human readable string.
780      *
781      * @param mode The mode.
782      * @return The string.
783      */
modeToString(int mode)784     private String modeToString(int mode) {
785         switch (mode) {
786             case AudioManager.MODE_INVALID:
787                 return MODE_DESCRIPTION_INVALID;
788             case AudioManager.MODE_CURRENT:
789                 return MODE_DESCRIPTION_CURRENT;
790             case AudioManager.MODE_NORMAL:
791                 return MODE_DESCRIPTION_NORMAL;
792             case AudioManager.MODE_RINGTONE:
793                 return MODE_DESCRIPTION_RINGTONE;
794             case AudioManager.MODE_IN_CALL:
795                 return MODE_DESCRIPTION_IN_CALL;
796             case AudioManager.MODE_IN_COMMUNICATION:
797                 return MODE_DESCRIPTION_IN_COMMUNICATION;
798             default:
799                 return "MODE_OTHER_" + mode;
800         }
801     }
802 
803     /**
804      * Dumps the state of the {@link CallAudioManager}.
805      *
806      * @param pw The {@code IndentingPrintWriter} to write the state to.
807      */
dump(IndentingPrintWriter pw)808     public void dump(IndentingPrintWriter pw) {
809         pw.println("mAudioState: " + mCallAudioState);
810         pw.println("mBluetoothManager:");
811         pw.increaseIndent();
812         mBluetoothManager.dump(pw);
813         pw.decreaseIndent();
814         if (mWiredHeadsetManager != null) {
815             pw.println("mWiredHeadsetManager:");
816             pw.increaseIndent();
817             mWiredHeadsetManager.dump(pw);
818             pw.decreaseIndent();
819         } else {
820             pw.println("mWiredHeadsetManager: null");
821         }
822         pw.println("mAudioFocusStreamType: " + streamTypeToString(mAudioFocusStreamType));
823         pw.println("mIsRinging: " + mIsRinging);
824         pw.println("mIsTonePlaying: " + mIsTonePlaying);
825         pw.println("mWasSpeakerOn: " + mWasSpeakerOn);
826         pw.println("mMostRecentlyUsedMode: " + modeToString(mMostRecentlyUsedMode));
827     }
828 }
829