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