1810735e3f0a4fe924a805981d32b6916ec834b38Sailesh Nepal/* 2810735e3f0a4fe924a805981d32b6916ec834b38Sailesh Nepal * Copyright (C) 2014 The Android Open Source Project 3810735e3f0a4fe924a805981d32b6916ec834b38Sailesh Nepal * 4810735e3f0a4fe924a805981d32b6916ec834b38Sailesh Nepal * Licensed under the Apache License, Version 2.0 (the "License"); 5810735e3f0a4fe924a805981d32b6916ec834b38Sailesh Nepal * you may not use this file except in compliance with the License. 6810735e3f0a4fe924a805981d32b6916ec834b38Sailesh Nepal * You may obtain a copy of the License at 7810735e3f0a4fe924a805981d32b6916ec834b38Sailesh Nepal * 8810735e3f0a4fe924a805981d32b6916ec834b38Sailesh Nepal * http://www.apache.org/licenses/LICENSE-2.0 9810735e3f0a4fe924a805981d32b6916ec834b38Sailesh Nepal * 10810735e3f0a4fe924a805981d32b6916ec834b38Sailesh Nepal * Unless required by applicable law or agreed to in writing, software 11810735e3f0a4fe924a805981d32b6916ec834b38Sailesh Nepal * distributed under the License is distributed on an "AS IS" BASIS, 12810735e3f0a4fe924a805981d32b6916ec834b38Sailesh Nepal * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13810735e3f0a4fe924a805981d32b6916ec834b38Sailesh Nepal * See the License for the specific language governing permissions and 14810735e3f0a4fe924a805981d32b6916ec834b38Sailesh Nepal * limitations under the License. 15810735e3f0a4fe924a805981d32b6916ec834b38Sailesh Nepal */ 16810735e3f0a4fe924a805981d32b6916ec834b38Sailesh Nepal 177cc70b4f0ad1064a4a0dce6056ad82b205887160Tyler Gunnpackage com.android.server.telecom; 18810735e3f0a4fe924a805981d32b6916ec834b38Sailesh Nepal 19810735e3f0a4fe924a805981d32b6916ec834b38Sailesh Nepalimport android.content.Context; 20810735e3f0a4fe924a805981d32b6916ec834b38Sailesh Nepalimport android.media.AudioManager; 217cc70b4f0ad1064a4a0dce6056ad82b205887160Tyler Gunnimport android.telecom.AudioState; 227cc70b4f0ad1064a4a0dce6056ad82b205887160Tyler Gunnimport android.telecom.CallState; 23810735e3f0a4fe924a805981d32b6916ec834b38Sailesh Nepal 2491d43cf9c985cc5a83795f256ef5c46ebb8fbdc1Tyler Gunnimport com.android.internal.util.Preconditions; 251ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon 2614ff8384ea44488ed6b203ceaac56a6c5294130aSantos Cordonimport java.util.Objects; 2714ff8384ea44488ed6b203ceaac56a6c5294130aSantos Cordon 28810735e3f0a4fe924a805981d32b6916ec834b38Sailesh Nepal/** 29810735e3f0a4fe924a805981d32b6916ec834b38Sailesh Nepal * This class manages audio modes, streams and other properties. 30810735e3f0a4fe924a805981d32b6916ec834b38Sailesh Nepal */ 31b88795aadc636d14156791c2fc93af051d7e0d49Sailesh Nepalfinal class CallAudioManager extends CallsManagerListenerBase 32b88795aadc636d14156791c2fc93af051d7e0d49Sailesh Nepal implements WiredHeadsetManager.Listener { 336aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal private static final int STREAM_NONE = -1; 341ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon 35deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordon private final StatusBarNotifier mStatusBarNotifier; 366aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal private final AudioManager mAudioManager; 37c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon private final BluetoothManager mBluetoothManager; 38b88795aadc636d14156791c2fc93af051d7e0d49Sailesh Nepal private final WiredHeadsetManager mWiredHeadsetManager; 39deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordon 406fb37c87836b5245046bd3b14320823ab839a10cIhab Awad private AudioState mAudioState; 416aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal private int mAudioFocusStreamType; 426aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal private boolean mIsRinging; 43a56f276cafcad80ab3a28996b6a0d72cffa4b2bcSantos Cordon private boolean mIsTonePlaying; 446aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal private boolean mWasSpeakerOn; 4514ff8384ea44488ed6b203ceaac56a6c5294130aSantos Cordon private int mMostRecentlyUsedMode = AudioManager.MODE_IN_CALL; 461ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon 47b88795aadc636d14156791c2fc93af051d7e0d49Sailesh Nepal CallAudioManager(Context context, StatusBarNotifier statusBarNotifier, 48b88795aadc636d14156791c2fc93af051d7e0d49Sailesh Nepal WiredHeadsetManager wiredHeadsetManager) { 49deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordon mStatusBarNotifier = statusBarNotifier; 506aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); 51c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon mBluetoothManager = new BluetoothManager(context, this); 52b88795aadc636d14156791c2fc93af051d7e0d49Sailesh Nepal mWiredHeadsetManager = wiredHeadsetManager; 53d0a76aa8789effa81bc7ce640e607718e4b722fdSailesh Nepal mWiredHeadsetManager.addListener(this); 54b88795aadc636d14156791c2fc93af051d7e0d49Sailesh Nepal 55deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordon saveAudioState(getInitialAudioState(null)); 566aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal mAudioFocusStreamType = STREAM_NONE; 576aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal } 581ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon 596fb37c87836b5245046bd3b14320823ab839a10cIhab Awad AudioState getAudioState() { 606aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal return mAudioState; 616aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal } 621ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon 631ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon @Override 641ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon public void onCallAdded(Call call) { 6514ff8384ea44488ed6b203ceaac56a6c5294130aSantos Cordon onCallUpdated(call); 6614ff8384ea44488ed6b203ceaac56a6c5294130aSantos Cordon 6714ff8384ea44488ed6b203ceaac56a6c5294130aSantos Cordon if (hasFocus() && getForegroundCall() == call) { 6814ff8384ea44488ed6b203ceaac56a6c5294130aSantos Cordon if (!call.isIncoming()) { 6914ff8384ea44488ed6b203ceaac56a6c5294130aSantos Cordon // Unmute new outgoing call. 7014ff8384ea44488ed6b203ceaac56a6c5294130aSantos Cordon setSystemAudioState(false, mAudioState.route, mAudioState.supportedRouteMask); 7114ff8384ea44488ed6b203ceaac56a6c5294130aSantos Cordon } 721ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon } 731ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon } 741ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon 751ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon @Override 761ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon public void onCallRemoved(Call call) { 7714ff8384ea44488ed6b203ceaac56a6c5294130aSantos Cordon // If we didn't already have focus, there's nothing to do. 7814ff8384ea44488ed6b203ceaac56a6c5294130aSantos Cordon if (hasFocus()) { 7914ff8384ea44488ed6b203ceaac56a6c5294130aSantos Cordon if (CallsManager.getInstance().getCalls().isEmpty()) { 8014ff8384ea44488ed6b203ceaac56a6c5294130aSantos Cordon Log.v(this, "all calls removed, reseting system audio to default state"); 817c6a5ec5c039cb3cc2e19024910fbcba0ce6b4e4Santos Cordon setInitialAudioState(null, false /* force */); 8214ff8384ea44488ed6b203ceaac56a6c5294130aSantos Cordon mWasSpeakerOn = false; 8314ff8384ea44488ed6b203ceaac56a6c5294130aSantos Cordon } 8414ff8384ea44488ed6b203ceaac56a6c5294130aSantos Cordon updateAudioStreamAndMode(); 856aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal } 861ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon } 871ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon 88810735e3f0a4fe924a805981d32b6916ec834b38Sailesh Nepal @Override 896fb37c87836b5245046bd3b14320823ab839a10cIhab Awad public void onCallStateChanged(Call call, int oldState, int newState) { 9014ff8384ea44488ed6b203ceaac56a6c5294130aSantos Cordon onCallUpdated(call); 911ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon } 921ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon 931ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon @Override 941ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon public void onIncomingCallAnswered(Call call) { 95c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon int route = mAudioState.route; 96c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon 97c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon // We do two things: 98c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon // (1) If this is the first call, then we can to turn on bluetooth if available. 99c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon // (2) Unmute the audio for the new incoming call. 100c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon boolean isOnlyCall = CallsManager.getInstance().getCalls().size() == 1; 101c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon if (isOnlyCall && mBluetoothManager.isBluetoothAvailable()) { 102c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon mBluetoothManager.connectBluetoothAudio(); 1036fb37c87836b5245046bd3b14320823ab839a10cIhab Awad route = AudioState.ROUTE_BLUETOOTH; 104c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon } 105c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon 106c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon setSystemAudioState(false /* isMute */, route, mAudioState.supportedRouteMask); 1071ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon } 1081ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon 1091ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon @Override 1106aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal public void onForegroundCallChanged(Call oldForegroundCall, Call newForegroundCall) { 11114ff8384ea44488ed6b203ceaac56a6c5294130aSantos Cordon onCallUpdated(newForegroundCall); 1126aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal // Ensure that the foreground call knows about the latest audio state. 1136aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal updateAudioForForegroundCall(); 1141ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon } 1151ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon 1167e66957928c5c23a1028c8e2a2d7cf359cbfa44eSailesh Nepal @Override 1175be64bc46c23b614d5452ca398a6bb7a512f1887Andrew Lee public void onIsVoipAudioModeChanged(Call call) { 1187e66957928c5c23a1028c8e2a2d7cf359cbfa44eSailesh Nepal updateAudioStreamAndMode(); 1197e66957928c5c23a1028c8e2a2d7cf359cbfa44eSailesh Nepal } 1207e66957928c5c23a1028c8e2a2d7cf359cbfa44eSailesh Nepal 121b88795aadc636d14156791c2fc93af051d7e0d49Sailesh Nepal /** 122b88795aadc636d14156791c2fc93af051d7e0d49Sailesh Nepal * Updates the audio route when the headset plugged in state changes. For example, if audio is 123b88795aadc636d14156791c2fc93af051d7e0d49Sailesh Nepal * being routed over speakerphone and a headset is plugged in then switch to wired headset. 124b88795aadc636d14156791c2fc93af051d7e0d49Sailesh Nepal */ 125b88795aadc636d14156791c2fc93af051d7e0d49Sailesh Nepal @Override 126b88795aadc636d14156791c2fc93af051d7e0d49Sailesh Nepal public void onWiredHeadsetPluggedInChanged(boolean oldIsPluggedIn, boolean newIsPluggedIn) { 12714ff8384ea44488ed6b203ceaac56a6c5294130aSantos Cordon // This can happen even when there are no calls and we don't have focus. 12814ff8384ea44488ed6b203ceaac56a6c5294130aSantos Cordon if (!hasFocus()) { 12914ff8384ea44488ed6b203ceaac56a6c5294130aSantos Cordon return; 13014ff8384ea44488ed6b203ceaac56a6c5294130aSantos Cordon } 13114ff8384ea44488ed6b203ceaac56a6c5294130aSantos Cordon 1326fb37c87836b5245046bd3b14320823ab839a10cIhab Awad int newRoute = AudioState.ROUTE_EARPIECE; 133b88795aadc636d14156791c2fc93af051d7e0d49Sailesh Nepal if (newIsPluggedIn) { 1346fb37c87836b5245046bd3b14320823ab839a10cIhab Awad newRoute = AudioState.ROUTE_WIRED_HEADSET; 135b88795aadc636d14156791c2fc93af051d7e0d49Sailesh Nepal } else if (mWasSpeakerOn) { 136b88795aadc636d14156791c2fc93af051d7e0d49Sailesh Nepal Call call = getForegroundCall(); 137b88795aadc636d14156791c2fc93af051d7e0d49Sailesh Nepal if (call != null && call.isAlive()) { 138b88795aadc636d14156791c2fc93af051d7e0d49Sailesh Nepal // Restore the speaker state. 1396fb37c87836b5245046bd3b14320823ab839a10cIhab Awad newRoute = AudioState.ROUTE_SPEAKER; 140b88795aadc636d14156791c2fc93af051d7e0d49Sailesh Nepal } 141b88795aadc636d14156791c2fc93af051d7e0d49Sailesh Nepal } 142b88795aadc636d14156791c2fc93af051d7e0d49Sailesh Nepal setSystemAudioState(mAudioState.isMuted, newRoute, calculateSupportedRoutes()); 143b88795aadc636d14156791c2fc93af051d7e0d49Sailesh Nepal } 144b88795aadc636d14156791c2fc93af051d7e0d49Sailesh Nepal 145deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordon void toggleMute() { 146deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordon mute(!mAudioState.isMuted); 147deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordon } 148deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordon 1496aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal void mute(boolean shouldMute) { 15014ff8384ea44488ed6b203ceaac56a6c5294130aSantos Cordon if (!hasFocus()) { 15114ff8384ea44488ed6b203ceaac56a6c5294130aSantos Cordon return; 15214ff8384ea44488ed6b203ceaac56a6c5294130aSantos Cordon } 15314ff8384ea44488ed6b203ceaac56a6c5294130aSantos Cordon 1546aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal Log.v(this, "mute, shouldMute: %b", shouldMute); 1551ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon 1566aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal // Don't mute if there are any emergency calls. 1576aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal if (CallsManager.getInstance().hasEmergencyCall()) { 1586aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal shouldMute = false; 1596aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal Log.v(this, "ignoring mute for emergency call"); 1606aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal } 1616aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal 1626aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal if (mAudioState.isMuted != shouldMute) { 1636aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal setSystemAudioState(shouldMute, mAudioState.route, mAudioState.supportedRouteMask); 1646aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal } 1656aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal } 1661ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon 1676aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal /** 1686aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal * Changed the audio route, for example from earpiece to speaker phone. 1696aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal * 1706fb37c87836b5245046bd3b14320823ab839a10cIhab Awad * @param route The new audio route to use. See {@link AudioState}. 1716aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal */ 1726aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal void setAudioRoute(int route) { 17314ff8384ea44488ed6b203ceaac56a6c5294130aSantos Cordon // This can happen even when there are no calls and we don't have focus. 17414ff8384ea44488ed6b203ceaac56a6c5294130aSantos Cordon if (!hasFocus()) { 17514ff8384ea44488ed6b203ceaac56a6c5294130aSantos Cordon return; 17614ff8384ea44488ed6b203ceaac56a6c5294130aSantos Cordon } 17714ff8384ea44488ed6b203ceaac56a6c5294130aSantos Cordon 1786fb37c87836b5245046bd3b14320823ab839a10cIhab Awad Log.v(this, "setAudioRoute, route: %s", AudioState.audioRouteToString(route)); 1791ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon 1806aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal // Change ROUTE_WIRED_OR_EARPIECE to a single entry. 1816aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal int newRoute = selectWiredOrEarpiece(route, mAudioState.supportedRouteMask); 1821ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon 1836aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal // If route is unsupported, do nothing. 1846aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal if ((mAudioState.supportedRouteMask | newRoute) == 0) { 1856aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal Log.wtf(this, "Asking to set to a route that is unsupported: %d", newRoute); 1866aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal return; 1871ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon } 1881ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon 1896aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal if (mAudioState.route != newRoute) { 1906aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal // Remember the new speaker state so it can be restored when the user plugs and unplugs 1916aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal // a headset. 1926fb37c87836b5245046bd3b14320823ab839a10cIhab Awad mWasSpeakerOn = newRoute == AudioState.ROUTE_SPEAKER; 1936aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal setSystemAudioState(mAudioState.isMuted, newRoute, mAudioState.supportedRouteMask); 1941ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon } 1956aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal } 1961ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon 1976aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal void setIsRinging(boolean isRinging) { 1986aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal if (mIsRinging != isRinging) { 1996aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal Log.v(this, "setIsRinging %b -> %b", mIsRinging, isRinging); 2006aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal mIsRinging = isRinging; 2016aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal updateAudioStreamAndMode(); 2021ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon } 2031ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon } 2041ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon 2051ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon /** 206a56f276cafcad80ab3a28996b6a0d72cffa4b2bcSantos Cordon * Sets the tone playing status. Some tones can play even when there are no live calls and this 207a56f276cafcad80ab3a28996b6a0d72cffa4b2bcSantos Cordon * status indicates that we should keep audio focus even for tones that play beyond the life of 208a56f276cafcad80ab3a28996b6a0d72cffa4b2bcSantos Cordon * calls. 209a56f276cafcad80ab3a28996b6a0d72cffa4b2bcSantos Cordon * 210a56f276cafcad80ab3a28996b6a0d72cffa4b2bcSantos Cordon * @param isPlayingNew The status to set. 211a56f276cafcad80ab3a28996b6a0d72cffa4b2bcSantos Cordon */ 212a56f276cafcad80ab3a28996b6a0d72cffa4b2bcSantos Cordon void setIsTonePlaying(boolean isPlayingNew) { 213a56f276cafcad80ab3a28996b6a0d72cffa4b2bcSantos Cordon ThreadUtil.checkOnMainThread(); 214a56f276cafcad80ab3a28996b6a0d72cffa4b2bcSantos Cordon 215a56f276cafcad80ab3a28996b6a0d72cffa4b2bcSantos Cordon if (mIsTonePlaying != isPlayingNew) { 216a56f276cafcad80ab3a28996b6a0d72cffa4b2bcSantos Cordon Log.v(this, "mIsTonePlaying %b -> %b.", mIsTonePlaying, isPlayingNew); 217a56f276cafcad80ab3a28996b6a0d72cffa4b2bcSantos Cordon mIsTonePlaying = isPlayingNew; 218a56f276cafcad80ab3a28996b6a0d72cffa4b2bcSantos Cordon updateAudioStreamAndMode(); 219a56f276cafcad80ab3a28996b6a0d72cffa4b2bcSantos Cordon } 220a56f276cafcad80ab3a28996b6a0d72cffa4b2bcSantos Cordon } 221a56f276cafcad80ab3a28996b6a0d72cffa4b2bcSantos Cordon 222a56f276cafcad80ab3a28996b6a0d72cffa4b2bcSantos Cordon /** 223c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon * Updates the audio routing according to the bluetooth state. 224c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon */ 225c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon void onBluetoothStateChange(BluetoothManager bluetoothManager) { 22614ff8384ea44488ed6b203ceaac56a6c5294130aSantos Cordon // This can happen even when there are no calls and we don't have focus. 22714ff8384ea44488ed6b203ceaac56a6c5294130aSantos Cordon if (!hasFocus()) { 22814ff8384ea44488ed6b203ceaac56a6c5294130aSantos Cordon return; 22914ff8384ea44488ed6b203ceaac56a6c5294130aSantos Cordon } 23014ff8384ea44488ed6b203ceaac56a6c5294130aSantos Cordon 23131953644b056deb3da675a6305cd8d371a8d7d5aSantos Cordon int supportedRoutes = calculateSupportedRoutes(); 232c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon int newRoute = mAudioState.route; 233c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon if (bluetoothManager.isBluetoothAudioConnectedOrPending()) { 2346fb37c87836b5245046bd3b14320823ab839a10cIhab Awad newRoute = AudioState.ROUTE_BLUETOOTH; 2356fb37c87836b5245046bd3b14320823ab839a10cIhab Awad } else if (mAudioState.route == AudioState.ROUTE_BLUETOOTH) { 23631953644b056deb3da675a6305cd8d371a8d7d5aSantos Cordon newRoute = selectWiredOrEarpiece(AudioState.ROUTE_WIRED_OR_EARPIECE, supportedRoutes); 237c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon // Do not switch to speaker when bluetooth disconnects. 238c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon mWasSpeakerOn = false; 239c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon } 240c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon 24131953644b056deb3da675a6305cd8d371a8d7d5aSantos Cordon setSystemAudioState(mAudioState.isMuted, newRoute, supportedRoutes); 242c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon } 243c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon 244c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon boolean isBluetoothAudioOn() { 245c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon return mBluetoothManager.isBluetoothAudioConnected(); 246c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon } 247c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon 248c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon boolean isBluetoothDeviceAvailable() { 249c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon return mBluetoothManager.isBluetoothAvailable(); 250c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon } 251c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon 2526fb37c87836b5245046bd3b14320823ab839a10cIhab Awad private void saveAudioState(AudioState audioState) { 253deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordon mAudioState = audioState; 254deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordon mStatusBarNotifier.notifyMute(mAudioState.isMuted); 2556fb37c87836b5245046bd3b14320823ab839a10cIhab Awad mStatusBarNotifier.notifySpeakerphone(mAudioState.route == AudioState.ROUTE_SPEAKER); 256deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordon } 257deb8c89707c604d4f9f32e476a58bd10a68293ffSantos Cordon 25814ff8384ea44488ed6b203ceaac56a6c5294130aSantos Cordon private void onCallUpdated(Call call) { 25914ff8384ea44488ed6b203ceaac56a6c5294130aSantos Cordon boolean wasNotVoiceCall = mAudioFocusStreamType != AudioManager.STREAM_VOICE_CALL; 26014ff8384ea44488ed6b203ceaac56a6c5294130aSantos Cordon updateAudioStreamAndMode(); 26114ff8384ea44488ed6b203ceaac56a6c5294130aSantos Cordon 26214ff8384ea44488ed6b203ceaac56a6c5294130aSantos Cordon // If we transition from not voice call to voice call, we need to set an initial state. 26314ff8384ea44488ed6b203ceaac56a6c5294130aSantos Cordon if (wasNotVoiceCall && mAudioFocusStreamType == AudioManager.STREAM_VOICE_CALL) { 2647c6a5ec5c039cb3cc2e19024910fbcba0ce6b4e4Santos Cordon setInitialAudioState(call, true /* force */); 26514ff8384ea44488ed6b203ceaac56a6c5294130aSantos Cordon } 26614ff8384ea44488ed6b203ceaac56a6c5294130aSantos Cordon } 26714ff8384ea44488ed6b203ceaac56a6c5294130aSantos Cordon 2686aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal private void setSystemAudioState(boolean isMuted, int route, int supportedRouteMask) { 2697c6a5ec5c039cb3cc2e19024910fbcba0ce6b4e4Santos Cordon setSystemAudioState(false /* force */, isMuted, route, supportedRouteMask); 2707c6a5ec5c039cb3cc2e19024910fbcba0ce6b4e4Santos Cordon } 2717c6a5ec5c039cb3cc2e19024910fbcba0ce6b4e4Santos Cordon 2727c6a5ec5c039cb3cc2e19024910fbcba0ce6b4e4Santos Cordon private void setSystemAudioState( 2737c6a5ec5c039cb3cc2e19024910fbcba0ce6b4e4Santos Cordon boolean force, boolean isMuted, int route, int supportedRouteMask) { 27414ff8384ea44488ed6b203ceaac56a6c5294130aSantos Cordon if (!hasFocus()) { 27514ff8384ea44488ed6b203ceaac56a6c5294130aSantos Cordon return; 27614ff8384ea44488ed6b203ceaac56a6c5294130aSantos Cordon } 27714ff8384ea44488ed6b203ceaac56a6c5294130aSantos Cordon 2786fb37c87836b5245046bd3b14320823ab839a10cIhab Awad AudioState oldAudioState = mAudioState; 2796fb37c87836b5245046bd3b14320823ab839a10cIhab Awad saveAudioState(new AudioState(isMuted, route, supportedRouteMask)); 2807c6a5ec5c039cb3cc2e19024910fbcba0ce6b4e4Santos Cordon if (!force && Objects.equals(oldAudioState, mAudioState)) { 28114ff8384ea44488ed6b203ceaac56a6c5294130aSantos Cordon return; 28214ff8384ea44488ed6b203ceaac56a6c5294130aSantos Cordon } 2836aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal Log.i(this, "changing audio state from %s to %s", oldAudioState, mAudioState); 2846aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal 2856aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal // Mute. 2866aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal if (mAudioState.isMuted != mAudioManager.isMicrophoneMute()) { 2876aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal Log.i(this, "changing microphone mute state to: %b", mAudioState.isMuted); 2886aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal mAudioManager.setMicrophoneMute(mAudioState.isMuted); 2896aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal } 2901ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon 2916aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal // Audio route. 2926fb37c87836b5245046bd3b14320823ab839a10cIhab Awad if (mAudioState.route == AudioState.ROUTE_BLUETOOTH) { 293c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon turnOnSpeaker(false); 294c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon turnOnBluetooth(true); 2956fb37c87836b5245046bd3b14320823ab839a10cIhab Awad } else if (mAudioState.route == AudioState.ROUTE_SPEAKER) { 296c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon turnOnBluetooth(false); 297c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon turnOnSpeaker(true); 2986fb37c87836b5245046bd3b14320823ab839a10cIhab Awad } else if (mAudioState.route == AudioState.ROUTE_EARPIECE || 2996fb37c87836b5245046bd3b14320823ab839a10cIhab Awad mAudioState.route == AudioState.ROUTE_WIRED_HEADSET) { 300c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon turnOnBluetooth(false); 301c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon turnOnSpeaker(false); 3026aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal } 3036aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal 3046aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal if (!oldAudioState.equals(mAudioState)) { 3056aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal CallsManager.getInstance().onAudioStateChanged(oldAudioState, mAudioState); 3066aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal updateAudioForForegroundCall(); 3071ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon } 3081ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon } 3091ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon 310c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon private void turnOnSpeaker(boolean on) { 311c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon // Wired headset and earpiece work the same way 312c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon if (mAudioManager.isSpeakerphoneOn() != on) { 31314ff8384ea44488ed6b203ceaac56a6c5294130aSantos Cordon Log.i(this, "turning speaker phone %s", on); 314c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon mAudioManager.setSpeakerphoneOn(on); 315c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon } 316c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon } 317c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon 318c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon private void turnOnBluetooth(boolean on) { 319c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon if (mBluetoothManager.isBluetoothAvailable()) { 32014ff8384ea44488ed6b203ceaac56a6c5294130aSantos Cordon boolean isAlreadyOn = mBluetoothManager.isBluetoothAudioConnectedOrPending(); 321c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon if (on != isAlreadyOn) { 32214ff8384ea44488ed6b203ceaac56a6c5294130aSantos Cordon Log.i(this, "connecting bluetooth %s", on); 323c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon if (on) { 324c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon mBluetoothManager.connectBluetoothAudio(); 325c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon } else { 326c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon mBluetoothManager.disconnectBluetoothAudio(); 327c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon } 328c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon } 329c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon } 330c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon } 331c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon 3326aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal private void updateAudioStreamAndMode() { 333e5a7c921546825810996bd51414f251f005e0766Yorke Lee Log.i(this, "updateAudioStreamAndMode, mIsRinging: %b, mIsTonePlaying: %b", mIsRinging, 334a56f276cafcad80ab3a28996b6a0d72cffa4b2bcSantos Cordon mIsTonePlaying); 3356aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal if (mIsRinging) { 3366aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal requestAudioFocusAndSetMode(AudioManager.STREAM_RING, AudioManager.MODE_RINGTONE); 3376aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal } else { 3385ba7f27491e287f39a999ddd3d1ed6a7bad78272Santos Cordon Call call = getForegroundCall(); 3396aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal if (call != null) { 3405be64bc46c23b614d5452ca398a6bb7a512f1887Andrew Lee int mode = call.getIsVoipAudioMode() ? 3417e66957928c5c23a1028c8e2a2d7cf359cbfa44eSailesh Nepal AudioManager.MODE_IN_COMMUNICATION : AudioManager.MODE_IN_CALL; 3426aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal requestAudioFocusAndSetMode(AudioManager.STREAM_VOICE_CALL, mode); 343a56f276cafcad80ab3a28996b6a0d72cffa4b2bcSantos Cordon } else if (mIsTonePlaying) { 344a56f276cafcad80ab3a28996b6a0d72cffa4b2bcSantos Cordon // There is no call, however, we are still playing a tone, so keep focus. 34514ff8384ea44488ed6b203ceaac56a6c5294130aSantos Cordon // Since there is no call from which to determine the mode, use the most 34614ff8384ea44488ed6b203ceaac56a6c5294130aSantos Cordon // recently used mode instead. 347a56f276cafcad80ab3a28996b6a0d72cffa4b2bcSantos Cordon requestAudioFocusAndSetMode( 34814ff8384ea44488ed6b203ceaac56a6c5294130aSantos Cordon AudioManager.STREAM_VOICE_CALL, mMostRecentlyUsedMode); 34942afb97b25495a0ed2153738b1e4a0c3b4c17276Yorke Lee } else if (!hasRingingForegroundCall()) { 3506aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal abandonAudioFocus(); 35142afb97b25495a0ed2153738b1e4a0c3b4c17276Yorke Lee } else { 35242afb97b25495a0ed2153738b1e4a0c3b4c17276Yorke Lee // mIsRinging is false, but there is a foreground ringing call present. Don't 35342afb97b25495a0ed2153738b1e4a0c3b4c17276Yorke Lee // abandon audio focus immediately to prevent audio focus from getting lost between 35442afb97b25495a0ed2153738b1e4a0c3b4c17276Yorke Lee // the time it takes for the foreground call to transition from RINGING to ACTIVE/ 35542afb97b25495a0ed2153738b1e4a0c3b4c17276Yorke Lee // DISCONNECTED. When the call eventually transitions to the next state, audio 35642afb97b25495a0ed2153738b1e4a0c3b4c17276Yorke Lee // focus will be correctly abandoned by the if clause above. 3576aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal } 3586aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal } 3596aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal } 3601ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon 3616aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal private void requestAudioFocusAndSetMode(int stream, int mode) { 362e5a7c921546825810996bd51414f251f005e0766Yorke Lee Log.i(this, "requestAudioFocusAndSetMode, stream: %d -> %d", mAudioFocusStreamType, stream); 3636aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal Preconditions.checkState(stream != STREAM_NONE); 3641ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon 3655ba7f27491e287f39a999ddd3d1ed6a7bad78272Santos Cordon // Even if we already have focus, if the stream is different we update audio manager to give 3665ba7f27491e287f39a999ddd3d1ed6a7bad78272Santos Cordon // it a hint about the purpose of our focus. 3675ba7f27491e287f39a999ddd3d1ed6a7bad78272Santos Cordon if (mAudioFocusStreamType != stream) { 3686aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal Log.v(this, "requesting audio focus for stream: %d", stream); 3696aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal mAudioManager.requestAudioFocusForCall(stream, 3706aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal AudioManager.AUDIOFOCUS_GAIN_TRANSIENT); 3711ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon } 3726aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal mAudioFocusStreamType = stream; 37314ff8384ea44488ed6b203ceaac56a6c5294130aSantos Cordon 3746aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal setMode(mode); 3756aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal } 3761ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon 3776aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal private void abandonAudioFocus() { 37814ff8384ea44488ed6b203ceaac56a6c5294130aSantos Cordon if (hasFocus()) { 3796aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal setMode(AudioManager.MODE_NORMAL); 3806aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal Log.v(this, "abandoning audio focus"); 3816aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal mAudioManager.abandonAudioFocusForCall(); 3826aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal mAudioFocusStreamType = STREAM_NONE; 3836aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal } 3841ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon } 3851ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon 3861ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon /** 3871ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon * Sets the audio mode. 3881ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon * 3896aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal * @param newMode Mode constant from AudioManager.MODE_*. 3901ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon */ 3916aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal private void setMode(int newMode) { 39214ff8384ea44488ed6b203ceaac56a6c5294130aSantos Cordon Preconditions.checkState(hasFocus()); 3936aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal int oldMode = mAudioManager.getMode(); 3946aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal Log.v(this, "Request to change audio mode from %d to %d", oldMode, newMode); 39560b6e2830ee5c7d11c0fb8018595bf174d94a086Yorke Lee 3966aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal if (oldMode != newMode) { 39760b6e2830ee5c7d11c0fb8018595bf174d94a086Yorke Lee if (oldMode == AudioManager.MODE_IN_CALL && newMode == AudioManager.MODE_RINGTONE) { 39860b6e2830ee5c7d11c0fb8018595bf174d94a086Yorke Lee Log.i(this, "Transition from IN_CALL -> RINGTONE. Resetting to NORMAL first."); 39960b6e2830ee5c7d11c0fb8018595bf174d94a086Yorke Lee mAudioManager.setMode(AudioManager.MODE_NORMAL); 40060b6e2830ee5c7d11c0fb8018595bf174d94a086Yorke Lee } 4016aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal mAudioManager.setMode(newMode); 40214ff8384ea44488ed6b203ceaac56a6c5294130aSantos Cordon mMostRecentlyUsedMode = newMode; 4031ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon } 4041ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon } 4051ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon 4066aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal private int selectWiredOrEarpiece(int route, int supportedRouteMask) { 4076aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal // Since they are mutually exclusive and one is ALWAYS valid, we allow a special input of 4086aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal // ROUTE_WIRED_OR_EARPIECE so that callers dont have to make a call to check which is 4096aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal // supported before calling setAudioRoute. 4106fb37c87836b5245046bd3b14320823ab839a10cIhab Awad if (route == AudioState.ROUTE_WIRED_OR_EARPIECE) { 4116fb37c87836b5245046bd3b14320823ab839a10cIhab Awad route = AudioState.ROUTE_WIRED_OR_EARPIECE & supportedRouteMask; 4126aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal if (route == 0) { 4136aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal Log.wtf(this, "One of wired headset or earpiece should always be valid."); 4146aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal // assume earpiece in this case. 4156fb37c87836b5245046bd3b14320823ab839a10cIhab Awad route = AudioState.ROUTE_EARPIECE; 4161ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon } 4176aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal } 4186aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal return route; 4196aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal } 4206aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal 4216aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal private int calculateSupportedRoutes() { 4226fb37c87836b5245046bd3b14320823ab839a10cIhab Awad int routeMask = AudioState.ROUTE_SPEAKER; 4231ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon 4246aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal if (mWiredHeadsetManager.isPluggedIn()) { 4256fb37c87836b5245046bd3b14320823ab839a10cIhab Awad routeMask |= AudioState.ROUTE_WIRED_HEADSET; 4266aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal } else { 4276fb37c87836b5245046bd3b14320823ab839a10cIhab Awad routeMask |= AudioState.ROUTE_EARPIECE; 4281ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon } 4296aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal 430c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon if (mBluetoothManager.isBluetoothAvailable()) { 4316fb37c87836b5245046bd3b14320823ab839a10cIhab Awad routeMask |= AudioState.ROUTE_BLUETOOTH; 432c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon } 433c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon 4346aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal return routeMask; 4351ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon } 4361ae2b855daecb6829cf50690b1dfa2d55f62cf6dSantos Cordon 4376fb37c87836b5245046bd3b14320823ab839a10cIhab Awad private AudioState getInitialAudioState(Call call) { 4386aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal int supportedRouteMask = calculateSupportedRoutes(); 439c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon int route = selectWiredOrEarpiece( 4406fb37c87836b5245046bd3b14320823ab839a10cIhab Awad AudioState.ROUTE_WIRED_OR_EARPIECE, supportedRouteMask); 441c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon 442c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon // We want the UI to indicate that "bluetooth is in use" in two slightly different cases: 443c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon // (a) The obvious case: if a bluetooth headset is currently in use for an ongoing call. 444c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon // (b) The not-so-obvious case: if an incoming call is ringing, and we expect that audio 445c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon // *will* be routed to a bluetooth headset once the call is answered. In this case, just 446c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon // check if the headset is available. Note this only applies when we are dealing with 447c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon // the first call. 448c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon if (call != null && mBluetoothManager.isBluetoothAvailable()) { 449c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon switch(call.getState()) { 4506fb37c87836b5245046bd3b14320823ab839a10cIhab Awad case CallState.ACTIVE: 4516fb37c87836b5245046bd3b14320823ab839a10cIhab Awad case CallState.ON_HOLD: 4526fb37c87836b5245046bd3b14320823ab839a10cIhab Awad case CallState.DIALING: 4535753f309976589302e1e319b44d0bf2777bbb2a0Santos Cordon case CallState.CONNECTING: 4546fb37c87836b5245046bd3b14320823ab839a10cIhab Awad case CallState.RINGING: 4556fb37c87836b5245046bd3b14320823ab839a10cIhab Awad route = AudioState.ROUTE_BLUETOOTH; 456c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon break; 457c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon default: 458c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon break; 459c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon } 460c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon } 461c7e85d4fa0bb3325133a79d4c89f3149e0af430eSantos Cordon 4626fb37c87836b5245046bd3b14320823ab839a10cIhab Awad return new AudioState(false, route, supportedRouteMask); 4636aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal } 4646aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal 4657c6a5ec5c039cb3cc2e19024910fbcba0ce6b4e4Santos Cordon private void setInitialAudioState(Call call, boolean force) { 4666fb37c87836b5245046bd3b14320823ab839a10cIhab Awad AudioState audioState = getInitialAudioState(call); 46714ff8384ea44488ed6b203ceaac56a6c5294130aSantos Cordon Log.v(this, "setInitialAudioState %s, %s", audioState, call); 4687c6a5ec5c039cb3cc2e19024910fbcba0ce6b4e4Santos Cordon setSystemAudioState( 4697c6a5ec5c039cb3cc2e19024910fbcba0ce6b4e4Santos Cordon force, audioState.isMuted, audioState.route, audioState.supportedRouteMask); 4706aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal } 4716aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal 4726aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal private void updateAudioForForegroundCall() { 4736aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal Call call = CallsManager.getInstance().getForegroundCall(); 474c92c436d84de46bb85100df9138378d9ffe0f2f2Sailesh Nepal if (call != null && call.getConnectionService() != null) { 475c92c436d84de46bb85100df9138378d9ffe0f2f2Sailesh Nepal call.getConnectionService().onAudioStateChanged(call, mAudioState); 4766aca10a0efa2771ccdef5920f4276f0db4a7ee1fSailesh Nepal } 477810735e3f0a4fe924a805981d32b6916ec834b38Sailesh Nepal } 4785ba7f27491e287f39a999ddd3d1ed6a7bad78272Santos Cordon 4795ba7f27491e287f39a999ddd3d1ed6a7bad78272Santos Cordon /** 4805ba7f27491e287f39a999ddd3d1ed6a7bad78272Santos Cordon * Returns the current foreground call in order to properly set the audio mode. 4815ba7f27491e287f39a999ddd3d1ed6a7bad78272Santos Cordon */ 4825ba7f27491e287f39a999ddd3d1ed6a7bad78272Santos Cordon private Call getForegroundCall() { 4835ba7f27491e287f39a999ddd3d1ed6a7bad78272Santos Cordon Call call = CallsManager.getInstance().getForegroundCall(); 4845ba7f27491e287f39a999ddd3d1ed6a7bad78272Santos Cordon 4855ba7f27491e287f39a999ddd3d1ed6a7bad78272Santos Cordon // We ignore any foreground call that is in the ringing state because we deal with ringing 4865ba7f27491e287f39a999ddd3d1ed6a7bad78272Santos Cordon // calls exclusively through the mIsRinging variable set by {@link Ringer}. 4875ba7f27491e287f39a999ddd3d1ed6a7bad78272Santos Cordon if (call != null && call.getState() == CallState.RINGING) { 4885ba7f27491e287f39a999ddd3d1ed6a7bad78272Santos Cordon call = null; 4895ba7f27491e287f39a999ddd3d1ed6a7bad78272Santos Cordon } 4905ba7f27491e287f39a999ddd3d1ed6a7bad78272Santos Cordon return call; 4915ba7f27491e287f39a999ddd3d1ed6a7bad78272Santos Cordon } 49214ff8384ea44488ed6b203ceaac56a6c5294130aSantos Cordon 49342afb97b25495a0ed2153738b1e4a0c3b4c17276Yorke Lee private boolean hasRingingForegroundCall() { 49442afb97b25495a0ed2153738b1e4a0c3b4c17276Yorke Lee Call call = CallsManager.getInstance().getForegroundCall(); 49542afb97b25495a0ed2153738b1e4a0c3b4c17276Yorke Lee return call != null && call.getState() == CallState.RINGING; 49642afb97b25495a0ed2153738b1e4a0c3b4c17276Yorke Lee } 49742afb97b25495a0ed2153738b1e4a0c3b4c17276Yorke Lee 49814ff8384ea44488ed6b203ceaac56a6c5294130aSantos Cordon private boolean hasFocus() { 49914ff8384ea44488ed6b203ceaac56a6c5294130aSantos Cordon return mAudioFocusStreamType != STREAM_NONE; 50014ff8384ea44488ed6b203ceaac56a6c5294130aSantos Cordon } 501810735e3f0a4fe924a805981d32b6916ec834b38Sailesh Nepal} 502