CallAudioManager.java revision d68832a72667d1f1d7557d6003e9a3fe4705cece
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
17package com.android.server.telecom;
18
19import android.app.ActivityManagerNative;
20import android.content.Context;
21import android.content.pm.UserInfo;
22import android.media.AudioManager;
23import android.media.IAudioService;
24import android.os.Binder;
25import android.os.Handler;
26import android.os.IBinder;
27import android.os.Looper;
28import android.os.Message;
29import android.os.RemoteException;
30import android.os.ServiceManager;
31import android.os.UserHandle;
32import android.telecom.CallAudioState;
33
34import com.android.internal.util.IndentingPrintWriter;
35import com.android.internal.util.Preconditions;
36
37import java.util.Objects;
38
39/**
40 * This class manages audio modes, streams and other properties.
41 */
42final 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
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
192    CallAudioState getCallAudioState() {
193        return mCallAudioState;
194    }
195
196    @Override
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
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
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
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
254    public void onForegroundCallChanged(Call oldForegroundCall, Call newForegroundCall) {
255        onCallUpdated(newForegroundCall);
256
257        // Set the system audio state again in case the current route is not permitted by the new
258        // foreground call.
259        setSystemAudioState(mCallAudioState.isMuted(), mCallAudioState.getRoute(),
260                calculateSupportedRoutes(newForegroundCall));
261
262        // Ensure that the foreground call knows about the latest audio state.
263        updateAudioForForegroundCall();
264    }
265
266    @Override
267    public void onIsVoipAudioModeChanged(Call call) {
268        updateAudioStreamAndMode(call);
269    }
270
271    /**
272      * Updates the audio route when the headset plugged in state changes. For example, if audio is
273      * being routed over speakerphone and a headset is plugged in then switch to wired headset.
274      */
275    @Override
276    public void onWiredHeadsetPluggedInChanged(boolean oldIsPluggedIn, boolean newIsPluggedIn) {
277        // This can happen even when there are no calls and we don't have focus.
278        if (!hasFocus()) {
279            return;
280        }
281
282        boolean isCurrentlyWiredHeadset = mCallAudioState.getRoute()
283                == CallAudioState.ROUTE_WIRED_HEADSET;
284
285        int newRoute = mCallAudioState.getRoute();  // start out with existing route
286        if (newIsPluggedIn) {
287            newRoute = CallAudioState.ROUTE_WIRED_HEADSET;
288        } else if (isCurrentlyWiredHeadset) {
289            Call call = getForegroundCall();
290            boolean hasLiveCall = call != null && call.isAlive();
291
292            if (hasLiveCall) {
293                // In order of preference when a wireless headset is unplugged.
294                if (mWasSpeakerOn) {
295                    newRoute = CallAudioState.ROUTE_SPEAKER;
296                } else {
297                    newRoute = CallAudioState.ROUTE_EARPIECE;
298                }
299
300                // We don't automatically connect to bluetooth when user unplugs their wired headset
301                // and they were previously using the wired. Wired and earpiece are effectively the
302                // same choice in that they replace each other as an option when wired headsets
303                // are plugged in and out. This means that keeping it earpiece is a bit more
304                // consistent with the status quo.  Bluetooth also has more danger associated with
305                // choosing it in the wrong curcumstance because bluetooth devices can be
306                // semi-public (like in a very-occupied car) where earpiece doesn't carry that risk.
307            }
308        }
309
310        // We need to call this every time even if we do not change the route because the supported
311        // routes changed either to include or not include WIRED_HEADSET.
312        setSystemAudioState(mCallAudioState.isMuted(), newRoute, calculateSupportedRoutes());
313    }
314
315    @Override
316    public void onDockChanged(boolean isDocked) {
317        // This can happen even when there are no calls and we don't have focus.
318        if (!hasFocus()) {
319            return;
320        }
321
322        if (isDocked) {
323            // Device just docked, turn to speakerphone. Only do so if the route is currently
324            // earpiece so that we dont switch out of a BT headset or a wired headset.
325            if (mCallAudioState.getRoute() == CallAudioState.ROUTE_EARPIECE) {
326                setAudioRoute(CallAudioState.ROUTE_SPEAKER);
327            }
328        } else {
329            // Device just undocked, remove from speakerphone if possible.
330            if (mCallAudioState.getRoute() == CallAudioState.ROUTE_SPEAKER) {
331                setAudioRoute(CallAudioState.ROUTE_WIRED_OR_EARPIECE);
332            }
333        }
334    }
335
336    void toggleMute() {
337        mute(!mCallAudioState.isMuted());
338    }
339
340    void mute(boolean shouldMute) {
341        if (!hasFocus()) {
342            return;
343        }
344
345        Log.v(this, "mute, shouldMute: %b", shouldMute);
346
347        // Don't mute if there are any emergency calls.
348        if (mCallsManager.hasEmergencyCall()) {
349            shouldMute = false;
350            Log.v(this, "ignoring mute for emergency call");
351        }
352
353        if (mCallAudioState.isMuted() != shouldMute) {
354            // We user CallsManager's foreground call so that we dont ignore ringing calls
355            // for logging purposes
356            Log.event(mCallsManager.getForegroundCall(), Log.Events.MUTE,
357                    shouldMute ? "on" : "off");
358
359            setSystemAudioState(shouldMute, mCallAudioState.getRoute(),
360                    mCallAudioState.getSupportedRouteMask());
361        }
362    }
363
364    /**
365     * Changed the audio route, for example from earpiece to speaker phone.
366     *
367     * @param route The new audio route to use. See {@link CallAudioState}.
368     */
369    void setAudioRoute(int route) {
370        // This can happen even when there are no calls and we don't have focus.
371        if (!hasFocus()) {
372            return;
373        }
374
375        Log.v(this, "setAudioRoute, route: %s", CallAudioState.audioRouteToString(route));
376
377        // Change ROUTE_WIRED_OR_EARPIECE to a single entry.
378        int newRoute = selectWiredOrEarpiece(route, mCallAudioState.getSupportedRouteMask());
379
380        // If route is unsupported, do nothing.
381        if ((mCallAudioState.getSupportedRouteMask() | newRoute) == 0) {
382            Log.wtf(this, "Asking to set to a route that is unsupported: %d", newRoute);
383            return;
384        }
385
386        if (mCallAudioState.getRoute() != newRoute) {
387            // Remember the new speaker state so it can be restored when the user plugs and unplugs
388            // a headset.
389            mWasSpeakerOn = newRoute == CallAudioState.ROUTE_SPEAKER;
390            setSystemAudioState(mCallAudioState.isMuted(), newRoute,
391                    mCallAudioState.getSupportedRouteMask());
392        }
393    }
394
395    /**
396     * Sets the audio stream and mode based on whether a call is ringing.
397     *
398     * @param call The call which changed ringing state.
399     * @param isRinging {@code true} if the call is ringing, {@code false} otherwise.
400     */
401    void setIsRinging(Call call, boolean isRinging) {
402        if (mIsRinging != isRinging) {
403            Log.i(this, "setIsRinging %b -> %b (call = %s)", mIsRinging, isRinging, call);
404            mIsRinging = isRinging;
405            updateAudioStreamAndMode(call);
406        }
407    }
408
409    /**
410     * Sets the tone playing status. Some tones can play even when there are no live calls and this
411     * status indicates that we should keep audio focus even for tones that play beyond the life of
412     * calls.
413     *
414     * @param isPlayingNew The status to set.
415     */
416    void setIsTonePlaying(boolean isPlayingNew) {
417        if (mIsTonePlaying != isPlayingNew) {
418            Log.v(this, "mIsTonePlaying %b -> %b.", mIsTonePlaying, isPlayingNew);
419            mIsTonePlaying = isPlayingNew;
420            updateAudioStreamAndMode();
421        }
422    }
423
424    /**
425     * Updates the audio routing according to the bluetooth state.
426     */
427    void onBluetoothStateChange(BluetoothManager bluetoothManager) {
428        // This can happen even when there are no calls and we don't have focus.
429        if (!hasFocus()) {
430            return;
431        }
432
433        int supportedRoutes = calculateSupportedRoutes();
434        int newRoute = mCallAudioState.getRoute();
435        if (bluetoothManager.isBluetoothAudioConnectedOrPending()) {
436            newRoute = CallAudioState.ROUTE_BLUETOOTH;
437        } else if (mCallAudioState.getRoute() == CallAudioState.ROUTE_BLUETOOTH) {
438            newRoute = selectWiredOrEarpiece(CallAudioState.ROUTE_WIRED_OR_EARPIECE,
439                    supportedRoutes);
440            // Do not switch to speaker when bluetooth disconnects.
441            mWasSpeakerOn = false;
442        }
443
444        setSystemAudioState(mCallAudioState.isMuted(), newRoute, supportedRoutes);
445    }
446
447    boolean isBluetoothAudioOn() {
448        return mBluetoothManager.isBluetoothAudioConnected();
449    }
450
451    boolean isBluetoothDeviceAvailable() {
452        return mBluetoothManager.isBluetoothAvailable();
453    }
454
455    private void saveAudioState(CallAudioState callAudioState) {
456        mCallAudioState = callAudioState;
457        mStatusBarNotifier.notifyMute(mCallAudioState.isMuted());
458        mStatusBarNotifier.notifySpeakerphone(mCallAudioState.getRoute()
459                == CallAudioState.ROUTE_SPEAKER);
460    }
461
462    private void onCallUpdated(Call call) {
463        updateAudioStreamAndMode(call);
464        if (call != null && call.getState() == CallState.ACTIVE &&
465                            call == mCallToSpeedUpMTAudio) {
466            mCallToSpeedUpMTAudio = null;
467        }
468    }
469
470    private void setSystemAudioState(boolean isMuted, int route, int supportedRouteMask) {
471        setSystemAudioState(false /* force */, isMuted, route, supportedRouteMask);
472    }
473
474    private void setSystemAudioState(
475            boolean force, boolean isMuted, int route, int supportedRouteMask) {
476        if (!hasFocus()) {
477            return;
478        }
479
480        CallAudioState oldAudioState = mCallAudioState;
481
482        // If the currently selected route is not supported, switch to another supported route
483        if ((route & supportedRouteMask) == 0) {
484            if ((CallAudioState.ROUTE_EARPIECE & supportedRouteMask) != 0) {
485                route = CallAudioState.ROUTE_EARPIECE;
486            } else if ((CallAudioState.ROUTE_WIRED_HEADSET & supportedRouteMask) != 0) {
487                route = CallAudioState.ROUTE_WIRED_HEADSET;
488            } else if ((CallAudioState.ROUTE_SPEAKER & supportedRouteMask) != 0) {
489                route = CallAudioState.ROUTE_SPEAKER;
490            } else if ((CallAudioState.ROUTE_BLUETOOTH & supportedRouteMask) != 0) {
491                route = CallAudioState.ROUTE_BLUETOOTH;
492            }
493        }
494
495        saveAudioState(new CallAudioState(isMuted, route, supportedRouteMask));
496        if (!force && Objects.equals(oldAudioState, mCallAudioState)) {
497            return;
498        }
499
500        Log.i(this, "setSystemAudioState: changing from %s to %s", oldAudioState, mCallAudioState);
501        Log.event(mCallsManager.getForegroundCall(), Log.Events.AUDIO_ROUTE,
502                CallAudioState.audioRouteToString(mCallAudioState.getRoute()));
503
504        mAudioManagerHandler.obtainMessage(
505                MSG_AUDIO_MANAGER_SET_MICROPHONE_MUTE,
506                mCallAudioState.isMuted() ? 1 : 0,
507                0)
508                .sendToTarget();
509
510        // Audio route.
511        if (mCallAudioState.getRoute() == CallAudioState.ROUTE_BLUETOOTH) {
512            turnOnSpeaker(false);
513            turnOnBluetooth(true);
514        } else if (mCallAudioState.getRoute() == CallAudioState.ROUTE_SPEAKER) {
515            turnOnBluetooth(false);
516            turnOnSpeaker(true);
517        } else if (mCallAudioState.getRoute() == CallAudioState.ROUTE_EARPIECE ||
518                mCallAudioState.getRoute() == CallAudioState.ROUTE_WIRED_HEADSET) {
519            turnOnBluetooth(false);
520            turnOnSpeaker(false);
521        }
522
523        if (!oldAudioState.equals(mCallAudioState)) {
524            mCallsManager.onCallAudioStateChanged(oldAudioState, mCallAudioState);
525            updateAudioForForegroundCall();
526        }
527    }
528
529    private void turnOnSpeaker(boolean on) {
530        mAudioManagerHandler.obtainMessage(MSG_AUDIO_MANAGER_TURN_ON_SPEAKER, on ? 1 : 0, 0)
531                .sendToTarget();
532    }
533
534    private void turnOnBluetooth(boolean on) {
535        if (mBluetoothManager.isBluetoothAvailable()) {
536            boolean isAlreadyOn = mBluetoothManager.isBluetoothAudioConnectedOrPending();
537            if (on != isAlreadyOn) {
538                Log.i(this, "connecting bluetooth %s", on);
539                if (on) {
540                    mBluetoothManager.connectBluetoothAudio();
541                } else {
542                    mBluetoothManager.disconnectBluetoothAudio();
543                }
544            }
545        }
546    }
547
548    private void updateAudioStreamAndMode() {
549        updateAudioStreamAndMode(null /* call */);
550    }
551
552    private void updateAudioStreamAndMode(Call callToUpdate) {
553        Log.i(this, "updateAudioStreamAndMode :  mIsRinging: %b, mIsTonePlaying: %b, call: %s",
554                mIsRinging, mIsTonePlaying, callToUpdate);
555
556        boolean wasVoiceCall = mAudioFocusStreamType == AudioManager.STREAM_VOICE_CALL;
557        if (mIsRinging) {
558            Log.i(this, "updateAudioStreamAndMode : ringing");
559            requestAudioFocusAndSetMode(AudioManager.STREAM_RING, AudioManager.MODE_RINGTONE);
560        } else {
561            Call foregroundCall = getForegroundCall();
562            Call waitingForAccountSelectionCall = mCallsManager
563                    .getFirstCallWithState(CallState.SELECT_PHONE_ACCOUNT);
564            Call call = mCallsManager.getForegroundCall();
565            if (foregroundCall == null && call != null && call == mCallToSpeedUpMTAudio) {
566                Log.v(this, "updateAudioStreamAndMode : no foreground, speeding up MT audio.");
567                requestAudioFocusAndSetMode(AudioManager.STREAM_VOICE_CALL,
568                                                         AudioManager.MODE_IN_CALL);
569            } else if (foregroundCall != null && !foregroundCall.isDisconnected() &&
570                    waitingForAccountSelectionCall == null) {
571                // In the case where there is a call that is waiting for account selection,
572                // this will fall back to abandonAudioFocus() below, which temporarily exits
573                // the in-call audio mode. This is to allow TalkBack to speak the "Call with"
574                // dialog information at media volume as opposed to through the earpiece.
575                // Once exiting the "Call with" dialog, the audio focus will return to an in-call
576                // audio mode when this method (updateAudioStreamAndMode) is called again.
577                int mode = foregroundCall.getIsVoipAudioMode() ?
578                        AudioManager.MODE_IN_COMMUNICATION : AudioManager.MODE_IN_CALL;
579                Log.v(this, "updateAudioStreamAndMode : foreground");
580                requestAudioFocusAndSetMode(AudioManager.STREAM_VOICE_CALL, mode);
581            } else if (mIsTonePlaying) {
582                // There is no call, however, we are still playing a tone, so keep focus.
583                // Since there is no call from which to determine the mode, use the most
584                // recently used mode instead.
585                Log.v(this, "updateAudioStreamAndMode : tone playing");
586                requestAudioFocusAndSetMode(
587                        AudioManager.STREAM_VOICE_CALL, mMostRecentlyUsedMode);
588            } else if (!hasRingingForegroundCall() && mCallsManager.hasOnlyDisconnectedCalls()) {
589                Log.v(this, "updateAudioStreamAndMode : no ringing call");
590                abandonAudioFocus();
591            } else {
592                // mIsRinging is false, but there is a foreground ringing call present. Don't
593                // abandon audio focus immediately to prevent audio focus from getting lost between
594                // the time it takes for the foreground call to transition from RINGING to ACTIVE/
595                // DISCONNECTED. When the call eventually transitions to the next state, audio
596                // focus will be correctly abandoned by the if clause above.
597            }
598        }
599
600        boolean isVoiceCall = mAudioFocusStreamType == AudioManager.STREAM_VOICE_CALL;
601
602        // If we transition from not a voice call to a voice call, we need to set an initial audio
603        // state for the call.
604        if (!wasVoiceCall && isVoiceCall) {
605            setInitialAudioState(callToUpdate, true /* force */);
606        }
607    }
608
609    private void requestAudioFocusAndSetMode(int stream, int mode) {
610        Log.v(this, "requestAudioFocusAndSetMode : stream: %s -> %s, mode: %s",
611                streamTypeToString(mAudioFocusStreamType), streamTypeToString(stream),
612                modeToString(mode));
613        Preconditions.checkState(stream != STREAM_NONE);
614
615        // Even if we already have focus, if the stream is different we update audio manager to give
616        // it a hint about the purpose of our focus.
617        if (mAudioFocusStreamType != stream) {
618            Log.i(this, "requestAudioFocusAndSetMode : requesting stream: %s -> %s",
619                    streamTypeToString(mAudioFocusStreamType), streamTypeToString(stream));
620            mAudioManagerHandler.obtainMessage(
621                    MSG_AUDIO_MANAGER_REQUEST_AUDIO_FOCUS_FOR_CALL,
622                    stream,
623                    0)
624                    .sendToTarget();
625        }
626        mAudioFocusStreamType = stream;
627
628        setMode(mode);
629    }
630
631    private void abandonAudioFocus() {
632        if (hasFocus()) {
633            setMode(AudioManager.MODE_NORMAL);
634            Log.v(this, "abandoning audio focus");
635            mAudioManagerHandler.obtainMessage(MSG_AUDIO_MANAGER_ABANDON_AUDIO_FOCUS_FOR_CALL, 0, 0)
636                    .sendToTarget();
637            mAudioFocusStreamType = STREAM_NONE;
638            mCallToSpeedUpMTAudio = null;
639        }
640    }
641
642    /**
643     * Sets the audio mode.
644     *
645     * @param newMode Mode constant from AudioManager.MODE_*.
646     */
647    private void setMode(int newMode) {
648        Preconditions.checkState(hasFocus());
649        mAudioManagerHandler.obtainMessage(MSG_AUDIO_MANAGER_SET_MODE, newMode, 0).sendToTarget();
650    }
651
652    private int selectWiredOrEarpiece(int route, int supportedRouteMask) {
653        // Since they are mutually exclusive and one is ALWAYS valid, we allow a special input of
654        // ROUTE_WIRED_OR_EARPIECE so that callers dont have to make a call to check which is
655        // supported before calling setAudioRoute.
656        if (route == CallAudioState.ROUTE_WIRED_OR_EARPIECE) {
657            route = CallAudioState.ROUTE_WIRED_OR_EARPIECE & supportedRouteMask;
658            if (route == 0) {
659                Log.w(this, "One of wired headset or earpiece should always be valid.");
660                // assume speaker in this case. This covers the watch case
661                route = CallAudioState.ROUTE_SPEAKER;
662            }
663        }
664        return route;
665    }
666
667    private int calculateSupportedRoutes() {
668        return calculateSupportedRoutes(getForegroundCall());
669    }
670
671    private int calculateSupportedRoutes(Call call) {
672        int routeMask = CallAudioState.ROUTE_SPEAKER;
673
674        if (mWiredHeadsetManager.isPluggedIn()) {
675            routeMask |= CallAudioState.ROUTE_WIRED_HEADSET;
676        } else {
677            routeMask |= CallAudioState.ROUTE_EARPIECE;
678        }
679
680        if (mBluetoothManager.isBluetoothAvailable()) {
681            routeMask |=  CallAudioState.ROUTE_BLUETOOTH;
682        }
683
684        return call != null ? routeMask & call.getSupportedAudioRoutes() : routeMask;
685    }
686
687    private CallAudioState getInitialAudioState(Call call) {
688        int supportedRouteMask = calculateSupportedRoutes(call);
689        int route = selectWiredOrEarpiece(
690                CallAudioState.ROUTE_WIRED_OR_EARPIECE, supportedRouteMask);
691
692        // We want the UI to indicate that "bluetooth is in use" in two slightly different cases:
693        // (a) The obvious case: if a bluetooth headset is currently in use for an ongoing call.
694        // (b) The not-so-obvious case: if an incoming call is ringing, and we expect that audio
695        //     *will* be routed to a bluetooth headset once the call is answered. In this case, just
696        //     check if the headset is available. Note this only applies when we are dealing with
697        //     the first call.
698        if (call != null && mBluetoothManager.isBluetoothAvailable()) {
699            switch(call.getState()) {
700                case CallState.ACTIVE:
701                case CallState.ON_HOLD:
702                case CallState.DIALING:
703                case CallState.CONNECTING:
704                case CallState.RINGING:
705                    route = CallAudioState.ROUTE_BLUETOOTH;
706                    break;
707                default:
708                    break;
709            }
710        }
711
712        return new CallAudioState(false, route, supportedRouteMask);
713    }
714
715    private void setInitialAudioState(Call call, boolean force) {
716        CallAudioState audioState = getInitialAudioState(call);
717        Log.i(this, "setInitialAudioState : audioState = %s, call = %s", audioState, call);
718        setSystemAudioState(
719                force, audioState.isMuted(), audioState.getRoute(),
720                audioState.getSupportedRouteMask());
721    }
722
723    private void updateAudioForForegroundCall() {
724        Call call = mCallsManager.getForegroundCall();
725        if (call != null && call.getConnectionService() != null) {
726            call.getConnectionService().onCallAudioStateChanged(call, mCallAudioState);
727        }
728    }
729
730    /**
731     * Returns the current foreground call in order to properly set the audio mode.
732     */
733    private Call getForegroundCall() {
734        Call call = mCallsManager.getForegroundCall();
735
736        // We ignore any foreground call that is in the ringing state because we deal with ringing
737        // calls exclusively through the mIsRinging variable set by {@link Ringer}.
738        if (call != null && call.getState() == CallState.RINGING) {
739            return null;
740        }
741
742        return call;
743    }
744
745    private boolean hasRingingForegroundCall() {
746        Call call = mCallsManager.getForegroundCall();
747        return call != null && call.getState() == CallState.RINGING;
748    }
749
750    private boolean hasFocus() {
751        return mAudioFocusStreamType != STREAM_NONE;
752    }
753
754    private IAudioService getAudioService() {
755        return IAudioService.Stub.asInterface(ServiceManager.getService(Context.AUDIO_SERVICE));
756    }
757
758    private int getCurrentUserId() {
759        final long ident = Binder.clearCallingIdentity();
760        try {
761            UserInfo currentUser = ActivityManagerNative.getDefault().getCurrentUser();
762            return currentUser.id;
763        } catch (RemoteException e) {
764            // Activity manager not running, nothing we can do assume user 0.
765        } finally {
766            Binder.restoreCallingIdentity(ident);
767        }
768        return UserHandle.USER_OWNER;
769    }
770
771    /**
772     * Translates an {@link AudioManager} stream type to a human-readable string description.
773     *
774     * @param streamType The stream type.
775     * @return Human readable description.
776     */
777    private String streamTypeToString(int streamType) {
778        switch (streamType) {
779            case STREAM_NONE:
780                return STREAM_DESCRIPTION_NONE;
781            case AudioManager.STREAM_ALARM:
782                return STREAM_DESCRIPTION_ALARM;
783            case AudioManager.STREAM_BLUETOOTH_SCO:
784                return STREAM_DESCRIPTION_BLUETOOTH_SCO;
785            case AudioManager.STREAM_DTMF:
786                return STREAM_DESCRIPTION_DTMF;
787            case AudioManager.STREAM_MUSIC:
788                return STREAM_DESCRIPTION_MUSIC;
789            case AudioManager.STREAM_NOTIFICATION:
790                return STREAM_DESCRIPTION_NOTIFICATION;
791            case AudioManager.STREAM_RING:
792                return STREAM_DESCRIPTION_RING;
793            case AudioManager.STREAM_SYSTEM:
794                return STREAM_DESCRIPTION_SYSTEM;
795            case AudioManager.STREAM_VOICE_CALL:
796                return STREAM_DESCRIPTION_VOICE_CALL;
797            default:
798                return "STEAM_OTHER_" + streamType;
799        }
800    }
801
802    /**
803     * Translates an {@link AudioManager} mode into a human readable string.
804     *
805     * @param mode The mode.
806     * @return The string.
807     */
808    private String modeToString(int mode) {
809        switch (mode) {
810            case AudioManager.MODE_INVALID:
811                return MODE_DESCRIPTION_INVALID;
812            case AudioManager.MODE_CURRENT:
813                return MODE_DESCRIPTION_CURRENT;
814            case AudioManager.MODE_NORMAL:
815                return MODE_DESCRIPTION_NORMAL;
816            case AudioManager.MODE_RINGTONE:
817                return MODE_DESCRIPTION_RINGTONE;
818            case AudioManager.MODE_IN_CALL:
819                return MODE_DESCRIPTION_IN_CALL;
820            case AudioManager.MODE_IN_COMMUNICATION:
821                return MODE_DESCRIPTION_IN_COMMUNICATION;
822            default:
823                return "MODE_OTHER_" + mode;
824        }
825    }
826
827    /**
828     * Dumps the state of the {@link CallAudioManager}.
829     *
830     * @param pw The {@code IndentingPrintWriter} to write the state to.
831     */
832    public void dump(IndentingPrintWriter pw) {
833        pw.println("mAudioState: " + mCallAudioState);
834        pw.println("mBluetoothManager:");
835        pw.increaseIndent();
836        mBluetoothManager.dump(pw);
837        pw.decreaseIndent();
838        if (mWiredHeadsetManager != null) {
839            pw.println("mWiredHeadsetManager:");
840            pw.increaseIndent();
841            mWiredHeadsetManager.dump(pw);
842            pw.decreaseIndent();
843        } else {
844            pw.println("mWiredHeadsetManager: null");
845        }
846        pw.println("mAudioFocusStreamType: " + streamTypeToString(mAudioFocusStreamType));
847        pw.println("mIsRinging: " + mIsRinging);
848        pw.println("mIsTonePlaying: " + mIsTonePlaying);
849        pw.println("mWasSpeakerOn: " + mWasSpeakerOn);
850        pw.println("mMostRecentlyUsedMode: " + modeToString(mMostRecentlyUsedMode));
851    }
852}
853