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