1 /* 2 * Copyright (C) 2015 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 20 import android.app.ActivityManager; 21 import android.bluetooth.BluetoothDevice; 22 import android.content.BroadcastReceiver; 23 import android.content.Context; 24 import android.content.Intent; 25 import android.content.IntentFilter; 26 import android.content.pm.UserInfo; 27 import android.media.AudioDeviceInfo; 28 import android.media.AudioManager; 29 import android.media.IAudioService; 30 import android.os.Binder; 31 import android.os.Handler; 32 import android.os.Looper; 33 import android.os.Message; 34 import android.os.RemoteException; 35 import android.os.UserHandle; 36 import android.telecom.CallAudioState; 37 import android.telecom.Log; 38 import android.telecom.Logging.Session; 39 import android.util.SparseArray; 40 41 import com.android.internal.annotations.VisibleForTesting; 42 import com.android.internal.os.SomeArgs; 43 import com.android.internal.util.IState; 44 import com.android.internal.util.IndentingPrintWriter; 45 import com.android.internal.util.State; 46 import com.android.internal.util.StateMachine; 47 import com.android.server.telecom.bluetooth.BluetoothRouteManager; 48 import com.android.server.telecom.flags.FeatureFlags; 49 50 import java.util.Collection; 51 import java.util.HashMap; 52 import java.util.Objects; 53 import java.util.Set; 54 import java.util.concurrent.Executor; 55 56 /** 57 * This class describes the available routes of a call as a state machine. 58 * Transitions are caused solely by the commands sent as messages. Possible values for msg.what 59 * are defined as event constants in this file. 60 * 61 * The eight states are all instances of the abstract base class, {@link AudioState}. Each state 62 * is a combination of one of the four audio routes (earpiece, wired headset, bluetooth, and 63 * speakerphone) and audio focus status (active or quiescent). 64 * 65 * Messages are processed first by the processMessage method in the base class, AudioState. 66 * Any messages not completely handled by AudioState are further processed by the same method in 67 * the route-specific abstract classes: {@link EarpieceRoute}, {@link HeadsetRoute}, 68 * {@link BluetoothRoute}, and {@link SpeakerRoute}. Finally, messages that are not handled at 69 * this level are then processed by the classes corresponding to the state instances themselves. 70 * 71 * There are several variables carrying additional state. These include: 72 * mAvailableRoutes: A bitmask describing which audio routes are available 73 * mWasOnSpeaker: A boolean indicating whether we should switch to speakerphone after disconnecting 74 * from a wired headset 75 * mIsMuted: a boolean indicating whether the audio is muted 76 */ 77 public class CallAudioRouteStateMachine extends StateMachine implements CallAudioRouteAdapter { 78 79 public static class Factory { create( Context context, CallsManager callsManager, BluetoothRouteManager bluetoothManager, WiredHeadsetManager wiredHeadsetManager, StatusBarNotifier statusBarNotifier, CallAudioManager.AudioServiceFactory audioServiceFactory, int earpieceControl, Executor asyncTaskExecutor, CallAudioCommunicationDeviceTracker communicationDeviceTracker, FeatureFlags featureFlags)80 public CallAudioRouteStateMachine create( 81 Context context, 82 CallsManager callsManager, 83 BluetoothRouteManager bluetoothManager, 84 WiredHeadsetManager wiredHeadsetManager, 85 StatusBarNotifier statusBarNotifier, 86 CallAudioManager.AudioServiceFactory audioServiceFactory, 87 int earpieceControl, 88 Executor asyncTaskExecutor, 89 CallAudioCommunicationDeviceTracker communicationDeviceTracker, 90 FeatureFlags featureFlags) { 91 return new CallAudioRouteStateMachine(context, 92 callsManager, 93 bluetoothManager, 94 wiredHeadsetManager, 95 statusBarNotifier, 96 audioServiceFactory, 97 earpieceControl, 98 asyncTaskExecutor, 99 communicationDeviceTracker, 100 featureFlags); 101 } 102 } 103 /** Values for CallAudioRouteStateMachine constructor's earPieceRouting arg. */ 104 public static final int EARPIECE_FORCE_DISABLED = 0; 105 public static final int EARPIECE_FORCE_ENABLED = 1; 106 public static final int EARPIECE_AUTO_DETECT = 2; 107 108 /** Direct the audio stream through the device's earpiece. */ 109 public static final int ROUTE_EARPIECE = CallAudioState.ROUTE_EARPIECE; 110 111 /** Direct the audio stream through Bluetooth. */ 112 public static final int ROUTE_BLUETOOTH = CallAudioState.ROUTE_BLUETOOTH; 113 114 /** Direct the audio stream through a wired headset. */ 115 public static final int ROUTE_WIRED_HEADSET = CallAudioState.ROUTE_WIRED_HEADSET; 116 117 /** Direct the audio stream through the device's speakerphone. */ 118 public static final int ROUTE_SPEAKER = CallAudioState.ROUTE_SPEAKER; 119 120 /** Direct the audio stream through another device. */ 121 public static final int ROUTE_STREAMING = CallAudioState.ROUTE_STREAMING; 122 123 /** Valid values for the first argument for SWITCH_BASELINE_ROUTE */ 124 public static final int NO_INCLUDE_BLUETOOTH_IN_BASELINE = 0; 125 public static final int INCLUDE_BLUETOOTH_IN_BASELINE = 1; 126 127 @VisibleForTesting 128 public static final SparseArray<String> AUDIO_ROUTE_TO_LOG_EVENT = new SparseArray<String>() {{ 129 put(CallAudioState.ROUTE_BLUETOOTH, LogUtils.Events.AUDIO_ROUTE_BT); 130 put(CallAudioState.ROUTE_EARPIECE, LogUtils.Events.AUDIO_ROUTE_EARPIECE); 131 put(CallAudioState.ROUTE_SPEAKER, LogUtils.Events.AUDIO_ROUTE_SPEAKER); 132 put(CallAudioState.ROUTE_WIRED_HEADSET, LogUtils.Events.AUDIO_ROUTE_HEADSET); 133 }}; 134 135 private static final String ACTIVE_EARPIECE_ROUTE_NAME = "ActiveEarpieceRoute"; 136 private static final String ACTIVE_BLUETOOTH_ROUTE_NAME = "ActiveBluetoothRoute"; 137 private static final String ACTIVE_SPEAKER_ROUTE_NAME = "ActiveSpeakerRoute"; 138 private static final String ACTIVE_HEADSET_ROUTE_NAME = "ActiveHeadsetRoute"; 139 private static final String RINGING_BLUETOOTH_ROUTE_NAME = "RingingBluetoothRoute"; 140 private static final String QUIESCENT_EARPIECE_ROUTE_NAME = "QuiescentEarpieceRoute"; 141 private static final String QUIESCENT_BLUETOOTH_ROUTE_NAME = "QuiescentBluetoothRoute"; 142 private static final String QUIESCENT_SPEAKER_ROUTE_NAME = "QuiescentSpeakerRoute"; 143 private static final String QUIESCENT_HEADSET_ROUTE_NAME = "QuiescentHeadsetRoute"; 144 145 public static final String NAME = CallAudioRouteStateMachine.class.getName(); 146 147 @Override onPreHandleMessage(Message msg)148 protected void onPreHandleMessage(Message msg) { 149 if (msg.obj != null && msg.obj instanceof SomeArgs) { 150 Session session = (Session) ((SomeArgs) msg.obj).arg1; 151 String messageCodeName = MESSAGE_CODE_TO_NAME.get(msg.what, "unknown"); 152 Log.continueSession(session, "CARSM.pM_" + messageCodeName); 153 Log.i(this, "Message received: %s=%d, arg1=%d", messageCodeName, msg.what, msg.arg1); 154 } 155 } 156 157 @Override onPostHandleMessage(Message msg)158 protected void onPostHandleMessage(Message msg) { 159 Log.endSession(); 160 if (msg.obj != null && msg.obj instanceof SomeArgs) { 161 ((SomeArgs) msg.obj).recycle(); 162 } 163 } 164 165 abstract class AudioState extends State { 166 @Override enter()167 public void enter() { 168 super.enter(); 169 Log.addEvent(mCallsManager.getForegroundCall(), LogUtils.Events.AUDIO_ROUTE, 170 "Entering state " + getName()); 171 if (isActive()) { 172 Log.addEvent(mCallsManager.getForegroundCall(), 173 AUDIO_ROUTE_TO_LOG_EVENT.get(getRouteCode(), LogUtils.Events.AUDIO_ROUTE)); 174 } 175 } 176 177 @Override exit()178 public void exit() { 179 Log.addEvent(mCallsManager.getForegroundCall(), LogUtils.Events.AUDIO_ROUTE, 180 "Leaving state " + getName()); 181 super.exit(); 182 } 183 184 @Override processMessage(Message msg)185 public boolean processMessage(Message msg) { 186 int addedRoutes = 0; 187 int removedRoutes = 0; 188 boolean isHandled = NOT_HANDLED; 189 190 Log.i(this, "Processing message %s", 191 MESSAGE_CODE_TO_NAME.get(msg.what, Integer.toString(msg.what))); 192 switch (msg.what) { 193 case CONNECT_WIRED_HEADSET: 194 Log.addEvent(mCallsManager.getForegroundCall(), LogUtils.Events.AUDIO_ROUTE, 195 "Wired headset connected"); 196 removedRoutes |= ROUTE_EARPIECE; 197 addedRoutes |= ROUTE_WIRED_HEADSET; 198 break; 199 case DISCONNECT_WIRED_HEADSET: 200 Log.addEvent(mCallsManager.getForegroundCall(), LogUtils.Events.AUDIO_ROUTE, 201 "Wired headset disconnected"); 202 removedRoutes |= ROUTE_WIRED_HEADSET; 203 if (mDoesDeviceSupportEarpieceRoute) { 204 addedRoutes |= ROUTE_EARPIECE; 205 } 206 break; 207 case BT_ACTIVE_DEVICE_PRESENT: 208 Log.addEvent(mCallsManager.getForegroundCall(), LogUtils.Events.AUDIO_ROUTE, 209 "Bluetooth active device present"); 210 break; 211 case BT_ACTIVE_DEVICE_GONE: 212 Log.addEvent(mCallsManager.getForegroundCall(), LogUtils.Events.AUDIO_ROUTE, 213 "Bluetooth active device gone"); 214 break; 215 case BLUETOOTH_DEVICE_LIST_CHANGED: 216 Log.addEvent(mCallsManager.getForegroundCall(), LogUtils.Events.AUDIO_ROUTE, 217 "Bluetooth device list changed"); 218 Collection<BluetoothDevice> connectedDevices = 219 mBluetoothRouteManager.getConnectedDevices(); 220 if (connectedDevices.size() > 0) { 221 addedRoutes |= ROUTE_BLUETOOTH; 222 } else { 223 removedRoutes |= ROUTE_BLUETOOTH; 224 } 225 isHandled = HANDLED; 226 break; 227 case SWITCH_BASELINE_ROUTE: 228 sendInternalMessage(calculateBaselineRouteMessage(false, 229 msg.arg1 == INCLUDE_BLUETOOTH_IN_BASELINE)); 230 return HANDLED; 231 case USER_SWITCH_BASELINE_ROUTE: 232 sendInternalMessage(calculateBaselineRouteMessage(true, 233 msg.arg1 == INCLUDE_BLUETOOTH_IN_BASELINE)); 234 return HANDLED; 235 case USER_SWITCH_BLUETOOTH: 236 // If the user tries to switch to BT, reset the explicitly-switched-away flag. 237 mHasUserExplicitlyLeftBluetooth = false; 238 return NOT_HANDLED; 239 case SWITCH_FOCUS: 240 // Perform BT hearing aid active device caching/restoration 241 if (mAudioFocusType != NO_FOCUS && msg.arg1 == NO_FOCUS) { 242 mBluetoothRouteManager.restoreHearingAidDevice(); 243 } else if (mAudioFocusType == NO_FOCUS && msg.arg1 != NO_FOCUS) { 244 mBluetoothRouteManager.cacheHearingAidDevice(); 245 } 246 mAudioFocusType = msg.arg1; 247 return NOT_HANDLED; 248 default: 249 return NOT_HANDLED; 250 } 251 252 if (addedRoutes != 0 || removedRoutes != 0 253 || msg.what == BLUETOOTH_DEVICE_LIST_CHANGED) { 254 mAvailableRoutes = modifyRoutes(mAvailableRoutes, removedRoutes, addedRoutes, true); 255 mDeviceSupportedRoutes = modifyRoutes(mDeviceSupportedRoutes, removedRoutes, 256 addedRoutes, false); 257 updateSystemAudioState(); 258 } 259 260 return isHandled; 261 } 262 263 // Behavior will depend on whether the state is an active one or a quiescent one. updateSystemAudioState()264 abstract public void updateSystemAudioState(); isActive()265 abstract public boolean isActive(); getRouteCode()266 abstract public int getRouteCode(); 267 } 268 269 class ActiveEarpieceRoute extends EarpieceRoute { 270 @Override getName()271 public String getName() { 272 return ACTIVE_EARPIECE_ROUTE_NAME; 273 } 274 275 @Override isActive()276 public boolean isActive() { 277 return true; 278 } 279 280 @Override enter()281 public void enter() { 282 super.enter(); 283 setSpeakerphoneOn(false); 284 if (mFeatureFlags.callAudioCommunicationDeviceRefactor()) { 285 mCommunicationDeviceTracker.setCommunicationDevice( 286 AudioDeviceInfo.TYPE_BUILTIN_EARPIECE, null); 287 } 288 CallAudioState newState = new CallAudioState(mIsMuted, ROUTE_EARPIECE, 289 mAvailableRoutes, null, 290 mBluetoothRouteManager.getConnectedDevices()); 291 if (mFeatureFlags.earlyUpdateInternalCallAudioState()) { 292 updateInternalCallAudioState(); 293 setSystemAudioState(newState, true); 294 } else { 295 setSystemAudioState(newState, true); 296 updateInternalCallAudioState(); 297 } 298 } 299 300 @Override updateSystemAudioState()301 public void updateSystemAudioState() { 302 updateInternalCallAudioState(); 303 setSystemAudioState(mCurrentCallAudioState); 304 } 305 306 @Override processMessage(Message msg)307 public boolean processMessage(Message msg) { 308 if (super.processMessage(msg) == HANDLED) { 309 return HANDLED; 310 } 311 switch (msg.what) { 312 case SWITCH_EARPIECE: 313 case USER_SWITCH_EARPIECE: 314 case SPEAKER_OFF: 315 // Nothing to do here 316 return HANDLED; 317 case BT_AUDIO_CONNECTED: 318 transitionTo(mActiveBluetoothRoute); 319 return HANDLED; 320 case SWITCH_BLUETOOTH: 321 case USER_SWITCH_BLUETOOTH: 322 if ((mAvailableRoutes & ROUTE_BLUETOOTH) != 0) { 323 if (mFeatureFlags.callAudioCommunicationDeviceRefactor()) { 324 mCommunicationDeviceTracker.clearCommunicationDevice( 325 AudioDeviceInfo.TYPE_BUILTIN_EARPIECE); 326 } 327 if (mAudioFocusType == ACTIVE_FOCUS 328 || mBluetoothRouteManager.isInbandRingingEnabled()) { 329 String address = (msg.obj instanceof SomeArgs) ? 330 (String) ((SomeArgs) msg.obj).arg2 : null; 331 // Omit transition to ActiveBluetoothRoute 332 setBluetoothOn(address); 333 } else { 334 transitionTo(mRingingBluetoothRoute); 335 } 336 } else { 337 Log.w(this, "Ignoring switch to bluetooth command. Not available."); 338 } 339 return HANDLED; 340 case SWITCH_HEADSET: 341 case USER_SWITCH_HEADSET: 342 if ((mAvailableRoutes & ROUTE_WIRED_HEADSET) != 0) { 343 if (mFeatureFlags.callAudioCommunicationDeviceRefactor()) { 344 mCommunicationDeviceTracker.clearCommunicationDevice( 345 AudioDeviceInfo.TYPE_BUILTIN_EARPIECE); 346 } 347 transitionTo(mActiveHeadsetRoute); 348 } else { 349 Log.w(this, "Ignoring switch to headset command. Not available."); 350 } 351 return HANDLED; 352 case CONNECT_DOCK: 353 // fall through; we want to switch to speaker mode when docked and in a call. 354 case SWITCH_SPEAKER: 355 case USER_SWITCH_SPEAKER: 356 if (mFeatureFlags.callAudioCommunicationDeviceRefactor()) { 357 mCommunicationDeviceTracker.clearCommunicationDevice( 358 AudioDeviceInfo.TYPE_BUILTIN_EARPIECE); 359 } 360 setSpeakerphoneOn(true); 361 // fall through 362 case SPEAKER_ON: 363 transitionTo(mActiveSpeakerRoute); 364 return HANDLED; 365 case SWITCH_FOCUS: 366 if (msg.arg1 == NO_FOCUS) { 367 reinitialize(); 368 mCallAudioManager.notifyAudioOperationsComplete(); 369 } 370 return HANDLED; 371 default: 372 return NOT_HANDLED; 373 } 374 } 375 } 376 377 class QuiescentEarpieceRoute extends EarpieceRoute { 378 @Override getName()379 public String getName() { 380 return QUIESCENT_EARPIECE_ROUTE_NAME; 381 } 382 383 @Override isActive()384 public boolean isActive() { 385 return false; 386 } 387 388 @Override enter()389 public void enter() { 390 super.enter(); 391 mHasUserExplicitlyLeftBluetooth = false; 392 updateInternalCallAudioState(); 393 } 394 395 @Override updateSystemAudioState()396 public void updateSystemAudioState() { 397 updateInternalCallAudioState(); 398 } 399 400 @Override processMessage(Message msg)401 public boolean processMessage(Message msg) { 402 if (super.processMessage(msg) == HANDLED) { 403 return HANDLED; 404 } 405 switch (msg.what) { 406 case SWITCH_EARPIECE: 407 case USER_SWITCH_EARPIECE: 408 case SPEAKER_ON: 409 // Ignore speakerphone state changes outside of calls. 410 case SPEAKER_OFF: 411 // Nothing to do here 412 return HANDLED; 413 case BT_AUDIO_CONNECTED: 414 Log.w(this, "BT Audio came on in quiescent earpiece route."); 415 transitionTo(mActiveBluetoothRoute); 416 return HANDLED; 417 case SWITCH_BLUETOOTH: 418 case USER_SWITCH_BLUETOOTH: 419 if ((mAvailableRoutes & ROUTE_BLUETOOTH) != 0) { 420 transitionTo(mQuiescentBluetoothRoute); 421 } else { 422 Log.w(this, "Ignoring switch to bluetooth command. Not available."); 423 } 424 return HANDLED; 425 case SWITCH_HEADSET: 426 case USER_SWITCH_HEADSET: 427 if ((mAvailableRoutes & ROUTE_WIRED_HEADSET) != 0) { 428 transitionTo(mQuiescentHeadsetRoute); 429 } else { 430 Log.w(this, "Ignoring switch to headset command. Not available."); 431 } 432 return HANDLED; 433 case CONNECT_DOCK: 434 // fall through; we want to go to the quiescent speaker route when out of a call 435 case SWITCH_SPEAKER: 436 case USER_SWITCH_SPEAKER: 437 transitionTo(mQuiescentSpeakerRoute); 438 return HANDLED; 439 case SWITCH_FOCUS: 440 if (msg.arg1 == ACTIVE_FOCUS || msg.arg1 == RINGING_FOCUS) { 441 transitionTo(mActiveEarpieceRoute); 442 } else { 443 mCallAudioManager.notifyAudioOperationsComplete(); 444 } 445 return HANDLED; 446 default: 447 return NOT_HANDLED; 448 } 449 } 450 } 451 452 abstract class EarpieceRoute extends AudioState { 453 @Override getRouteCode()454 public int getRouteCode() { 455 return CallAudioState.ROUTE_EARPIECE; 456 } 457 458 @Override processMessage(Message msg)459 public boolean processMessage(Message msg) { 460 if (super.processMessage(msg) == HANDLED) { 461 return HANDLED; 462 } 463 switch (msg.what) { 464 case CONNECT_WIRED_HEADSET: 465 sendInternalMessage(SWITCH_HEADSET); 466 return HANDLED; 467 case BT_ACTIVE_DEVICE_PRESENT: 468 if (!mHasUserExplicitlyLeftBluetooth) { 469 sendInternalMessage(SWITCH_BLUETOOTH); 470 } else { 471 Log.i(this, "Not switching to BT route from earpiece because user has " + 472 "explicitly disconnected."); 473 } 474 return HANDLED; 475 case BT_ACTIVE_DEVICE_GONE: 476 // No change in audio route required 477 return HANDLED; 478 case DISCONNECT_WIRED_HEADSET: 479 Log.e(this, new IllegalStateException(), 480 "Wired headset should not go from connected to not when on " + 481 "earpiece"); 482 return HANDLED; 483 case BT_AUDIO_DISCONNECTED: 484 // This may be sent as a confirmation by the BT stack after switch off BT. 485 return HANDLED; 486 case DISCONNECT_DOCK: 487 // Nothing to do here 488 return HANDLED; 489 case STREAMING_FORCE_ENABLED: 490 transitionTo(mStreamingState); 491 return HANDLED; 492 default: 493 return NOT_HANDLED; 494 } 495 } 496 } 497 498 class ActiveHeadsetRoute extends HeadsetRoute { 499 @Override getName()500 public String getName() { 501 return ACTIVE_HEADSET_ROUTE_NAME; 502 } 503 504 @Override isActive()505 public boolean isActive() { 506 return true; 507 } 508 509 @Override enter()510 public void enter() { 511 super.enter(); 512 setSpeakerphoneOn(false); 513 if (mFeatureFlags.callAudioCommunicationDeviceRefactor()) { 514 mCommunicationDeviceTracker.setCommunicationDevice( 515 AudioDeviceInfo.TYPE_WIRED_HEADSET, null); 516 } 517 CallAudioState newState = new CallAudioState(mIsMuted, ROUTE_WIRED_HEADSET, 518 mAvailableRoutes, null, mBluetoothRouteManager.getConnectedDevices()); 519 if (mFeatureFlags.earlyUpdateInternalCallAudioState()) { 520 updateInternalCallAudioState(); 521 setSystemAudioState(newState, true); 522 } else { 523 setSystemAudioState(newState, true); 524 updateInternalCallAudioState(); 525 } 526 } 527 528 @Override updateSystemAudioState()529 public void updateSystemAudioState() { 530 updateInternalCallAudioState(); 531 setSystemAudioState(mCurrentCallAudioState); 532 } 533 534 @Override processMessage(Message msg)535 public boolean processMessage(Message msg) { 536 if (super.processMessage(msg) == HANDLED) { 537 return HANDLED; 538 } 539 switch (msg.what) { 540 case SWITCH_EARPIECE: 541 case USER_SWITCH_EARPIECE: 542 if ((mAvailableRoutes & ROUTE_EARPIECE) != 0) { 543 if (mFeatureFlags.callAudioCommunicationDeviceRefactor()) { 544 mCommunicationDeviceTracker.clearCommunicationDevice( 545 AudioDeviceInfo.TYPE_WIRED_HEADSET); 546 } 547 transitionTo(mActiveEarpieceRoute); 548 } else { 549 Log.w(this, "Ignoring switch to earpiece command. Not available."); 550 } 551 return HANDLED; 552 case BT_AUDIO_CONNECTED: 553 transitionTo(mActiveBluetoothRoute); 554 return HANDLED; 555 case SWITCH_BLUETOOTH: 556 case USER_SWITCH_BLUETOOTH: 557 if ((mAvailableRoutes & ROUTE_BLUETOOTH) != 0) { 558 if (mAudioFocusType == ACTIVE_FOCUS 559 || mBluetoothRouteManager.isInbandRingingEnabled()) { 560 String address = (msg.obj instanceof SomeArgs) ? 561 (String) ((SomeArgs) msg.obj).arg2 : null; 562 if (mFeatureFlags.callAudioCommunicationDeviceRefactor()) { 563 mCommunicationDeviceTracker.clearCommunicationDevice( 564 AudioDeviceInfo.TYPE_WIRED_HEADSET); 565 } 566 // Omit transition to ActiveBluetoothRoute until actual connection. 567 setBluetoothOn(address); 568 } else { 569 transitionTo(mRingingBluetoothRoute); 570 } 571 } else { 572 Log.w(this, "Ignoring switch to bluetooth command. Not available."); 573 } 574 return HANDLED; 575 case SWITCH_HEADSET: 576 case USER_SWITCH_HEADSET: 577 case SPEAKER_OFF: 578 // Nothing to do 579 return HANDLED; 580 case SWITCH_SPEAKER: 581 case USER_SWITCH_SPEAKER: 582 if (mFeatureFlags.callAudioCommunicationDeviceRefactor()) { 583 mCommunicationDeviceTracker.clearCommunicationDevice( 584 AudioDeviceInfo.TYPE_WIRED_HEADSET); 585 } 586 setSpeakerphoneOn(true); 587 // fall through 588 case SPEAKER_ON: 589 transitionTo(mActiveSpeakerRoute); 590 return HANDLED; 591 case SWITCH_FOCUS: 592 if (msg.arg1 == NO_FOCUS) { 593 reinitialize(); 594 mCallAudioManager.notifyAudioOperationsComplete(); 595 } 596 return HANDLED; 597 case STREAMING_FORCE_ENABLED: 598 transitionTo(mStreamingState); 599 return HANDLED; 600 default: 601 return NOT_HANDLED; 602 } 603 } 604 } 605 606 class QuiescentHeadsetRoute extends HeadsetRoute { 607 @Override getName()608 public String getName() { 609 return QUIESCENT_HEADSET_ROUTE_NAME; 610 } 611 612 @Override isActive()613 public boolean isActive() { 614 return false; 615 } 616 617 @Override enter()618 public void enter() { 619 super.enter(); 620 mHasUserExplicitlyLeftBluetooth = false; 621 updateInternalCallAudioState(); 622 } 623 624 @Override updateSystemAudioState()625 public void updateSystemAudioState() { 626 updateInternalCallAudioState(); 627 } 628 629 @Override processMessage(Message msg)630 public boolean processMessage(Message msg) { 631 if (super.processMessage(msg) == HANDLED) { 632 return HANDLED; 633 } 634 switch (msg.what) { 635 case SWITCH_EARPIECE: 636 case USER_SWITCH_EARPIECE: 637 if ((mAvailableRoutes & ROUTE_EARPIECE) != 0) { 638 transitionTo(mQuiescentEarpieceRoute); 639 } else { 640 Log.w(this, "Ignoring switch to earpiece command. Not available."); 641 } 642 return HANDLED; 643 case BT_AUDIO_CONNECTED: 644 transitionTo(mActiveBluetoothRoute); 645 Log.w(this, "BT Audio came on in quiescent headset route."); 646 return HANDLED; 647 case SWITCH_BLUETOOTH: 648 case USER_SWITCH_BLUETOOTH: 649 if ((mAvailableRoutes & ROUTE_BLUETOOTH) != 0) { 650 transitionTo(mQuiescentBluetoothRoute); 651 } else { 652 Log.w(this, "Ignoring switch to bluetooth command. Not available."); 653 } 654 return HANDLED; 655 case SWITCH_HEADSET: 656 case USER_SWITCH_HEADSET: 657 case SPEAKER_ON: 658 // Ignore speakerphone state changes outside of calls. 659 case SPEAKER_OFF: 660 // Nothing to do 661 return HANDLED; 662 case SWITCH_SPEAKER: 663 case USER_SWITCH_SPEAKER: 664 transitionTo(mQuiescentSpeakerRoute); 665 return HANDLED; 666 case SWITCH_FOCUS: 667 if (msg.arg1 == ACTIVE_FOCUS || msg.arg1 == RINGING_FOCUS) { 668 transitionTo(mActiveHeadsetRoute); 669 } else { 670 mCallAudioManager.notifyAudioOperationsComplete(); 671 } 672 return HANDLED; 673 default: 674 return NOT_HANDLED; 675 } 676 } 677 } 678 679 abstract class HeadsetRoute extends AudioState { 680 @Override getRouteCode()681 public int getRouteCode() { 682 return CallAudioState.ROUTE_WIRED_HEADSET; 683 } 684 685 @Override processMessage(Message msg)686 public boolean processMessage(Message msg) { 687 if (super.processMessage(msg) == HANDLED) { 688 return HANDLED; 689 } 690 switch (msg.what) { 691 case CONNECT_WIRED_HEADSET: 692 Log.e(this, new IllegalStateException(), 693 "Wired headset should already be connected."); 694 return HANDLED; 695 case BT_ACTIVE_DEVICE_PRESENT: 696 if (!mHasUserExplicitlyLeftBluetooth) { 697 sendInternalMessage(SWITCH_BLUETOOTH); 698 } else { 699 Log.i(this, "Not switching to BT route from headset because user has " + 700 "explicitly disconnected."); 701 } 702 return HANDLED; 703 case BT_ACTIVE_DEVICE_GONE: 704 // No change in audio route required 705 return HANDLED; 706 case DISCONNECT_WIRED_HEADSET: 707 if (mWasOnSpeaker) { 708 setSpeakerphoneOn(true); 709 sendInternalMessage(SWITCH_SPEAKER); 710 } else { 711 sendInternalMessage(SWITCH_BASELINE_ROUTE, INCLUDE_BLUETOOTH_IN_BASELINE); 712 } 713 return HANDLED; 714 case BT_AUDIO_DISCONNECTED: 715 // This may be sent as a confirmation by the BT stack after switch off BT. 716 return HANDLED; 717 case CONNECT_DOCK: 718 // Nothing to do here 719 return HANDLED; 720 case DISCONNECT_DOCK: 721 // Nothing to do here 722 return HANDLED; 723 default: 724 return NOT_HANDLED; 725 } 726 } 727 } 728 729 // Note: transitions to/from this class work a bit differently -- we delegate to 730 // BluetoothRouteManager to manage all Bluetooth state, so instead of transitioning to one of 731 // the bluetooth states immediately when there's an request to do so, we wait for 732 // BluetoothRouteManager to report its state before we go into this state. 733 class ActiveBluetoothRoute extends BluetoothRoute { 734 @Override getName()735 public String getName() { 736 return ACTIVE_BLUETOOTH_ROUTE_NAME; 737 } 738 739 @Override isActive()740 public boolean isActive() { 741 return true; 742 } 743 744 @Override enter()745 public void enter() { 746 super.enter(); 747 setSpeakerphoneOn(false); 748 // Try arbitrarily connecting to BT audio if we haven't already. This handles 749 // the edge case of when the audio route is in a quiescent route while in-call and 750 // the BT connection fails to be set. Previously, the logic was to setBluetoothOn in 751 // ACTIVE_FOCUS but the route would still remain in a quiescent route, so instead we 752 // should be transitioning directly into the active route. 753 if (mFeatureFlags.callAudioCommunicationDeviceRefactor()) { 754 setBluetoothOn(null); 755 } 756 if (mFeatureFlags.updateRouteMaskWhenBtConnected()) { 757 mAvailableRoutes |= ROUTE_BLUETOOTH; 758 } 759 CallAudioState newState = new CallAudioState(mIsMuted, ROUTE_BLUETOOTH, 760 mAvailableRoutes, mBluetoothRouteManager.getBluetoothAudioConnectedDevice(), 761 mBluetoothRouteManager.getConnectedDevices()); 762 if (mFeatureFlags.earlyUpdateInternalCallAudioState()) { 763 updateInternalCallAudioState(); 764 setSystemAudioState(newState, true); 765 } else { 766 setSystemAudioState(newState, true); 767 updateInternalCallAudioState(); 768 } 769 // Do not send RINGER_MODE_CHANGE if no Bluetooth SCO audio device is available 770 if (mBluetoothRouteManager.getBluetoothAudioConnectedDevice() != null) { 771 mCallAudioManager.onRingerModeChange(); 772 } 773 } 774 775 @Override updateSystemAudioState()776 public void updateSystemAudioState() { 777 updateInternalCallAudioState(); 778 setSystemAudioState(mCurrentCallAudioState); 779 } 780 781 @Override handleBtInitiatedDisconnect()782 public void handleBtInitiatedDisconnect() { 783 // There's special-case state transitioning here -- if BT tells us that 784 // something got disconnected, we don't want to disconnect BT before 785 // transitioning, since BT might be trying to connect another device in the 786 // meantime. 787 int command = calculateBaselineRouteMessage(false, false); 788 switch (command) { 789 case SWITCH_EARPIECE: 790 transitionTo(mActiveEarpieceRoute); 791 break; 792 case SWITCH_HEADSET: 793 transitionTo(mActiveHeadsetRoute); 794 break; 795 case SWITCH_SPEAKER: 796 setSpeakerphoneOn(true); 797 transitionTo(mActiveSpeakerRoute); 798 break; 799 default: 800 Log.w(this, "Got unexpected code " + command + " when processing a" 801 + " BT-initiated audio disconnect"); 802 // Some fallback logic to make sure we make it off the bluetooth route. 803 super.handleBtInitiatedDisconnect(); 804 break; 805 } 806 } 807 808 @Override processMessage(Message msg)809 public boolean processMessage(Message msg) { 810 if (super.processMessage(msg) == HANDLED) { 811 return HANDLED; 812 } 813 switch (msg.what) { 814 case USER_SWITCH_EARPIECE: 815 mHasUserExplicitlyLeftBluetooth = true; 816 // fall through 817 case SWITCH_EARPIECE: 818 if ((mAvailableRoutes & ROUTE_EARPIECE) != 0) { 819 setBluetoothOff(); 820 transitionTo(mActiveEarpieceRoute); 821 } else { 822 Log.w(this, "Ignoring switch to earpiece command. Not available."); 823 } 824 return HANDLED; 825 case BT_AUDIO_CONNECTED: 826 // Send ringer mode change because we transit to ActiveBluetoothState even 827 // when HFP is connecting 828 mCallAudioManager.onRingerModeChange(); 829 // Update the in-call app on the new active BT device in case that changed. 830 updateSystemAudioState(); 831 return HANDLED; 832 case SWITCH_BLUETOOTH: 833 case USER_SWITCH_BLUETOOTH: 834 String address = (msg.obj instanceof SomeArgs) ? 835 (String) ((SomeArgs) msg.obj).arg2 : null; 836 setBluetoothOn(address); 837 return HANDLED; 838 case USER_SWITCH_HEADSET: 839 mHasUserExplicitlyLeftBluetooth = true; 840 // fall through 841 case SWITCH_HEADSET: 842 if ((mAvailableRoutes & ROUTE_WIRED_HEADSET) != 0) { 843 setBluetoothOff(); 844 transitionTo(mActiveHeadsetRoute); 845 } else { 846 Log.w(this, "Ignoring switch to headset command. Not available."); 847 } 848 return HANDLED; 849 case USER_SWITCH_SPEAKER: 850 mHasUserExplicitlyLeftBluetooth = true; 851 // fall through 852 case SWITCH_SPEAKER: 853 setSpeakerphoneOn(true); 854 // fall through 855 case SPEAKER_ON: 856 setBluetoothOff(); 857 transitionTo(mActiveSpeakerRoute); 858 return HANDLED; 859 case SPEAKER_OFF: 860 return HANDLED; 861 case SWITCH_FOCUS: 862 if (msg.arg1 == NO_FOCUS) { 863 // Only disconnect audio here instead of routing away from BT entirely. 864 if (mFeatureFlags.transitRouteBeforeAudioDisconnectBt()) { 865 // Note: We have to turn off mute here rather than when entering the 866 // QuiescentBluetooth route because setMuteOn will only work when there the 867 // current state is active. 868 // We don't need to do this in the unflagged path since reinitialize 869 // will turn off mute. 870 if (mFeatureFlags.resetMuteWhenEnteringQuiescentBtRoute()) { 871 setMuteOn(false); 872 } 873 transitionTo(mQuiescentBluetoothRoute); 874 mBluetoothRouteManager.disconnectAudio(); 875 } else { 876 mBluetoothRouteManager.disconnectAudio(); 877 reinitialize(); 878 } 879 mCallAudioManager.notifyAudioOperationsComplete(); 880 } else if (msg.arg1 == RINGING_FOCUS 881 && !mBluetoothRouteManager.isInbandRingingEnabled()) { 882 setBluetoothOff(); 883 transitionTo(mRingingBluetoothRoute); 884 } 885 return HANDLED; 886 case BT_AUDIO_DISCONNECTED: 887 handleBtInitiatedDisconnect(); 888 return HANDLED; 889 default: 890 return NOT_HANDLED; 891 } 892 } 893 } 894 895 // This state is only used when the device doesn't support in-band ring. If it does, 896 // ActiveBluetoothRoute is used instead. 897 class RingingBluetoothRoute extends BluetoothRoute { 898 @Override getName()899 public String getName() { 900 return RINGING_BLUETOOTH_ROUTE_NAME; 901 } 902 903 @Override isActive()904 public boolean isActive() { 905 return false; 906 } 907 908 @Override enter()909 public void enter() { 910 super.enter(); 911 setSpeakerphoneOn(false); 912 // Do not enable SCO audio here, since RING is being sent to the headset. 913 CallAudioState newState = new CallAudioState(mIsMuted, ROUTE_BLUETOOTH, 914 mAvailableRoutes, mBluetoothRouteManager.getBluetoothAudioConnectedDevice(), 915 mBluetoothRouteManager.getConnectedDevices()); 916 if (mFeatureFlags.earlyUpdateInternalCallAudioState()) { 917 updateInternalCallAudioState(); 918 setSystemAudioState(newState, true); 919 } else { 920 setSystemAudioState(newState, true); 921 updateInternalCallAudioState(); 922 } 923 } 924 925 @Override updateSystemAudioState()926 public void updateSystemAudioState() { 927 updateInternalCallAudioState(); 928 setSystemAudioState(mCurrentCallAudioState); 929 } 930 931 @Override processMessage(Message msg)932 public boolean processMessage(Message msg) { 933 if (super.processMessage(msg) == HANDLED) { 934 return HANDLED; 935 } 936 switch (msg.what) { 937 case USER_SWITCH_EARPIECE: 938 mHasUserExplicitlyLeftBluetooth = true; 939 // fall through 940 case SWITCH_EARPIECE: 941 if ((mAvailableRoutes & ROUTE_EARPIECE) != 0) { 942 transitionTo(mActiveEarpieceRoute); 943 } else { 944 Log.w(this, "Ignoring switch to earpiece command. Not available."); 945 } 946 return HANDLED; 947 case BT_AUDIO_CONNECTED: 948 transitionTo(mActiveBluetoothRoute); 949 return HANDLED; 950 case SWITCH_BLUETOOTH: 951 case USER_SWITCH_BLUETOOTH: 952 // Nothing to do 953 return HANDLED; 954 case USER_SWITCH_HEADSET: 955 mHasUserExplicitlyLeftBluetooth = true; 956 // fall through 957 case SWITCH_HEADSET: 958 if ((mAvailableRoutes & ROUTE_WIRED_HEADSET) != 0) { 959 transitionTo(mActiveHeadsetRoute); 960 } else { 961 Log.w(this, "Ignoring switch to headset command. Not available."); 962 } 963 return HANDLED; 964 case USER_SWITCH_SPEAKER: 965 mHasUserExplicitlyLeftBluetooth = true; 966 // fall through 967 case SWITCH_SPEAKER: 968 setSpeakerphoneOn(true); 969 // fall through 970 case SPEAKER_ON: 971 transitionTo(mActiveSpeakerRoute); 972 return HANDLED; 973 case SPEAKER_OFF: 974 return HANDLED; 975 case SWITCH_FOCUS: 976 if (msg.arg1 == NO_FOCUS) { 977 reinitialize(); 978 mCallAudioManager.notifyAudioOperationsComplete(); 979 } else if (msg.arg1 == ACTIVE_FOCUS) { 980 setBluetoothOn(null); 981 } 982 return HANDLED; 983 case BT_AUDIO_DISCONNECTED: 984 // Ignore this -- audio disconnecting while ringing w/o in-band should not 985 // cause a route switch, since the device is still connected. 986 return HANDLED; 987 default: 988 return NOT_HANDLED; 989 } 990 } 991 } 992 993 class QuiescentBluetoothRoute extends BluetoothRoute { 994 @Override getName()995 public String getName() { 996 return QUIESCENT_BLUETOOTH_ROUTE_NAME; 997 } 998 999 @Override isActive()1000 public boolean isActive() { 1001 return false; 1002 } 1003 1004 @Override enter()1005 public void enter() { 1006 super.enter(); 1007 mHasUserExplicitlyLeftBluetooth = false; 1008 updateInternalCallAudioState(); 1009 } 1010 1011 @Override updateSystemAudioState()1012 public void updateSystemAudioState() { 1013 updateInternalCallAudioState(); 1014 } 1015 1016 @Override processMessage(Message msg)1017 public boolean processMessage(Message msg) { 1018 if (super.processMessage(msg) == HANDLED) { 1019 return HANDLED; 1020 } 1021 switch (msg.what) { 1022 case SWITCH_EARPIECE: 1023 case USER_SWITCH_EARPIECE: 1024 if ((mAvailableRoutes & ROUTE_EARPIECE) != 0) { 1025 transitionTo(mQuiescentEarpieceRoute); 1026 } else { 1027 Log.w(this, "Ignoring switch to earpiece command. Not available."); 1028 } 1029 return HANDLED; 1030 case BT_AUDIO_CONNECTED: 1031 transitionTo(mActiveBluetoothRoute); 1032 return HANDLED; 1033 case SWITCH_BLUETOOTH: 1034 case USER_SWITCH_BLUETOOTH: 1035 case SPEAKER_ON: 1036 // Ignore speakerphone state changes outside of calls. 1037 case SPEAKER_OFF: 1038 // Nothing to do 1039 return HANDLED; 1040 case SWITCH_HEADSET: 1041 case USER_SWITCH_HEADSET: 1042 if ((mAvailableRoutes & ROUTE_WIRED_HEADSET) != 0) { 1043 transitionTo(mQuiescentHeadsetRoute); 1044 } else { 1045 Log.w(this, "Ignoring switch to headset command. Not available."); 1046 } 1047 return HANDLED; 1048 case SWITCH_SPEAKER: 1049 case USER_SWITCH_SPEAKER: 1050 transitionTo(mQuiescentSpeakerRoute); 1051 return HANDLED; 1052 case SWITCH_FOCUS: 1053 if (msg.arg1 == ACTIVE_FOCUS) { 1054 // It is possible that the connection to BT will fail while in-call, in 1055 // which case, we want to transition into the active route. 1056 if (mFeatureFlags.callAudioCommunicationDeviceRefactor()) { 1057 transitionTo(mActiveBluetoothRoute); 1058 } else { 1059 setBluetoothOn(null); 1060 } 1061 } else if (msg.arg1 == RINGING_FOCUS) { 1062 if (mBluetoothRouteManager.isInbandRingingEnabled()) { 1063 setBluetoothOn(null); 1064 } else { 1065 transitionTo(mRingingBluetoothRoute); 1066 } 1067 } else { 1068 mCallAudioManager.notifyAudioOperationsComplete(); 1069 } 1070 return HANDLED; 1071 case BT_AUDIO_DISCONNECTED: 1072 // Ignore this -- audio disconnecting while quiescent should not cause a 1073 // route switch, since the device is still connected. 1074 return HANDLED; 1075 default: 1076 return NOT_HANDLED; 1077 } 1078 } 1079 } 1080 1081 abstract class BluetoothRoute extends AudioState { 1082 @Override getRouteCode()1083 public int getRouteCode() { 1084 return CallAudioState.ROUTE_BLUETOOTH; 1085 } 1086 handleBtInitiatedDisconnect()1087 public void handleBtInitiatedDisconnect() { 1088 sendInternalMessage(SWITCH_BASELINE_ROUTE, NO_INCLUDE_BLUETOOTH_IN_BASELINE); 1089 } 1090 1091 @Override processMessage(Message msg)1092 public boolean processMessage(Message msg) { 1093 if (super.processMessage(msg) == HANDLED) { 1094 return HANDLED; 1095 } 1096 switch (msg.what) { 1097 case CONNECT_WIRED_HEADSET: 1098 sendInternalMessage(SWITCH_HEADSET); 1099 return HANDLED; 1100 case BT_ACTIVE_DEVICE_PRESENT: 1101 Log.w(this, "Bluetooth active device should not" 1102 + " have been null while we were in BT route."); 1103 return HANDLED; 1104 case BT_ACTIVE_DEVICE_GONE: 1105 handleBtInitiatedDisconnect(); 1106 mWasOnSpeaker = false; 1107 return HANDLED; 1108 case DISCONNECT_WIRED_HEADSET: 1109 // No change in audio route required 1110 return HANDLED; 1111 case CONNECT_DOCK: 1112 // Nothing to do here 1113 return HANDLED; 1114 case DISCONNECT_DOCK: 1115 // Nothing to do here 1116 return HANDLED; 1117 case STREAMING_FORCE_ENABLED: 1118 transitionTo(mStreamingState); 1119 return HANDLED; 1120 default: 1121 return NOT_HANDLED; 1122 } 1123 } 1124 } 1125 1126 class ActiveSpeakerRoute extends SpeakerRoute { 1127 @Override getName()1128 public String getName() { 1129 return ACTIVE_SPEAKER_ROUTE_NAME; 1130 } 1131 1132 @Override isActive()1133 public boolean isActive() { 1134 return true; 1135 } 1136 1137 @Override enter()1138 public void enter() { 1139 super.enter(); 1140 // Don't set speakerphone on here -- we might end up in this state by following 1141 // the speaker state that some other app commanded. 1142 mWasOnSpeaker = true; 1143 CallAudioState newState = new CallAudioState(mIsMuted, ROUTE_SPEAKER, 1144 mAvailableRoutes, null, mBluetoothRouteManager.getConnectedDevices()); 1145 if (mFeatureFlags.earlyUpdateInternalCallAudioState()) { 1146 updateInternalCallAudioState(); 1147 setSystemAudioState(newState, true); 1148 } else { 1149 setSystemAudioState(newState, true); 1150 updateInternalCallAudioState(); 1151 } 1152 } 1153 1154 @Override updateSystemAudioState()1155 public void updateSystemAudioState() { 1156 updateInternalCallAudioState(); 1157 setSystemAudioState(mCurrentCallAudioState); 1158 } 1159 1160 @Override processMessage(Message msg)1161 public boolean processMessage(Message msg) { 1162 if (super.processMessage(msg) == HANDLED) { 1163 return HANDLED; 1164 } 1165 switch(msg.what) { 1166 case USER_SWITCH_EARPIECE: 1167 mWasOnSpeaker = false; 1168 // fall through 1169 case SWITCH_EARPIECE: 1170 if ((mAvailableRoutes & ROUTE_EARPIECE) != 0) { 1171 transitionTo(mActiveEarpieceRoute); 1172 } else { 1173 Log.w(this, "Ignoring switch to earpiece command. Not available."); 1174 } 1175 return HANDLED; 1176 case BT_AUDIO_CONNECTED: 1177 transitionTo(mActiveBluetoothRoute); 1178 return HANDLED; 1179 case USER_SWITCH_BLUETOOTH: 1180 mWasOnSpeaker = false; 1181 // fall through 1182 case SWITCH_BLUETOOTH: 1183 String address = (msg.obj instanceof SomeArgs) ? 1184 (String) ((SomeArgs) msg.obj).arg2 : null; 1185 if ((mAvailableRoutes & ROUTE_BLUETOOTH) != 0) { 1186 if (mAudioFocusType == ACTIVE_FOCUS 1187 || mBluetoothRouteManager.isInbandRingingEnabled()) { 1188 // Omit transition to ActiveBluetoothRoute 1189 setBluetoothOn(address); 1190 } else { 1191 transitionTo(mRingingBluetoothRoute); 1192 } 1193 } else { 1194 Log.w(this, "Ignoring switch to bluetooth command. Not available."); 1195 } 1196 return HANDLED; 1197 case USER_SWITCH_HEADSET: 1198 mWasOnSpeaker = false; 1199 // fall through 1200 case SWITCH_HEADSET: 1201 if ((mAvailableRoutes & ROUTE_WIRED_HEADSET) != 0) { 1202 transitionTo(mActiveHeadsetRoute); 1203 } else { 1204 Log.w(this, "Ignoring switch to headset command. Not available."); 1205 } 1206 return HANDLED; 1207 case SWITCH_SPEAKER: 1208 case USER_SWITCH_SPEAKER: 1209 // Nothing to do 1210 return HANDLED; 1211 case SPEAKER_ON: 1212 // Expected, since we just transitioned here 1213 return HANDLED; 1214 case SPEAKER_OFF: 1215 // Check if we already requested to connect to other devices and just waiting 1216 // for their response. In some cases, this SPEAKER_OFF message may come in 1217 // before the response, we can just ignore the message here to not re-evaluate 1218 // the baseline route incorrectly 1219 if (!mBluetoothRouteManager.isBluetoothAudioConnectedOrPending()) { 1220 sendInternalMessage(SWITCH_BASELINE_ROUTE, INCLUDE_BLUETOOTH_IN_BASELINE); 1221 } 1222 return HANDLED; 1223 case SWITCH_FOCUS: 1224 if (msg.arg1 == NO_FOCUS) { 1225 reinitialize(); 1226 mCallAudioManager.notifyAudioOperationsComplete(); 1227 } 1228 return HANDLED; 1229 default: 1230 return NOT_HANDLED; 1231 } 1232 } 1233 } 1234 1235 class QuiescentSpeakerRoute extends SpeakerRoute { 1236 @Override getName()1237 public String getName() { 1238 return QUIESCENT_SPEAKER_ROUTE_NAME; 1239 } 1240 1241 @Override isActive()1242 public boolean isActive() { 1243 return false; 1244 } 1245 1246 @Override enter()1247 public void enter() { 1248 super.enter(); 1249 mHasUserExplicitlyLeftBluetooth = false; 1250 // Omit setting mWasOnSpeaker to true here, since this does not reflect a call 1251 // actually being on speakerphone. 1252 updateInternalCallAudioState(); 1253 } 1254 1255 @Override updateSystemAudioState()1256 public void updateSystemAudioState() { 1257 updateInternalCallAudioState(); 1258 } 1259 1260 @Override processMessage(Message msg)1261 public boolean processMessage(Message msg) { 1262 if (super.processMessage(msg) == HANDLED) { 1263 return HANDLED; 1264 } 1265 switch(msg.what) { 1266 case SWITCH_EARPIECE: 1267 case USER_SWITCH_EARPIECE: 1268 if ((mAvailableRoutes & ROUTE_EARPIECE) != 0) { 1269 transitionTo(mQuiescentEarpieceRoute); 1270 } else { 1271 Log.w(this, "Ignoring switch to earpiece command. Not available."); 1272 } 1273 return HANDLED; 1274 case BT_AUDIO_CONNECTED: 1275 transitionTo(mActiveBluetoothRoute); 1276 Log.w(this, "BT audio reported as connected while in quiescent speaker"); 1277 return HANDLED; 1278 case SWITCH_BLUETOOTH: 1279 case USER_SWITCH_BLUETOOTH: 1280 if ((mAvailableRoutes & ROUTE_BLUETOOTH) != 0) { 1281 transitionTo(mQuiescentBluetoothRoute); 1282 } else { 1283 Log.w(this, "Ignoring switch to bluetooth command. Not available."); 1284 } 1285 return HANDLED; 1286 case SWITCH_HEADSET: 1287 case USER_SWITCH_HEADSET: 1288 if ((mAvailableRoutes & ROUTE_WIRED_HEADSET) != 0) { 1289 transitionTo(mQuiescentHeadsetRoute); 1290 } else { 1291 Log.w(this, "Ignoring switch to headset command. Not available."); 1292 } 1293 return HANDLED; 1294 case SWITCH_SPEAKER: 1295 case USER_SWITCH_SPEAKER: 1296 case SPEAKER_ON: 1297 // Nothing to do 1298 return HANDLED; 1299 case DISCONNECT_DOCK: 1300 // Fall-through; same as if speaker goes off, we want to switch baseline. 1301 case SPEAKER_OFF: 1302 sendInternalMessage(SWITCH_BASELINE_ROUTE, INCLUDE_BLUETOOTH_IN_BASELINE); 1303 return HANDLED; 1304 case SWITCH_FOCUS: 1305 if (msg.arg1 == ACTIVE_FOCUS || msg.arg1 == RINGING_FOCUS) { 1306 setSpeakerphoneOn(true); 1307 transitionTo(mActiveSpeakerRoute); 1308 } else { 1309 mCallAudioManager.notifyAudioOperationsComplete(); 1310 } 1311 return HANDLED; 1312 default: 1313 return NOT_HANDLED; 1314 } 1315 } 1316 } 1317 1318 abstract class SpeakerRoute extends AudioState { 1319 @Override getRouteCode()1320 public int getRouteCode() { 1321 return CallAudioState.ROUTE_SPEAKER; 1322 } 1323 1324 @Override processMessage(Message msg)1325 public boolean processMessage(Message msg) { 1326 if (super.processMessage(msg) == HANDLED) { 1327 return HANDLED; 1328 } 1329 switch (msg.what) { 1330 case CONNECT_WIRED_HEADSET: 1331 sendInternalMessage(SWITCH_HEADSET); 1332 return HANDLED; 1333 case BT_ACTIVE_DEVICE_PRESENT: 1334 if (!mHasUserExplicitlyLeftBluetooth) { 1335 sendInternalMessage(SWITCH_BLUETOOTH); 1336 } else { 1337 Log.i(this, "Not switching to BT route from speaker because user has " + 1338 "explicitly disconnected."); 1339 } 1340 return HANDLED; 1341 case BT_ACTIVE_DEVICE_GONE: 1342 // No change in audio route required 1343 return HANDLED; 1344 case DISCONNECT_WIRED_HEADSET: 1345 // No change in audio route required 1346 return HANDLED; 1347 case BT_AUDIO_DISCONNECTED: 1348 // This may be sent as a confirmation by the BT stack after switch off BT. 1349 return HANDLED; 1350 case CONNECT_DOCK: 1351 // Nothing to do here 1352 return HANDLED; 1353 case DISCONNECT_DOCK: 1354 sendInternalMessage(SWITCH_BASELINE_ROUTE, INCLUDE_BLUETOOTH_IN_BASELINE); 1355 return HANDLED; 1356 case STREAMING_FORCE_ENABLED: 1357 transitionTo(mStreamingState); 1358 return HANDLED; 1359 default: 1360 return NOT_HANDLED; 1361 } 1362 } 1363 } 1364 1365 class StreamingState extends AudioState { 1366 @Override enter()1367 public void enter() { 1368 super.enter(); 1369 updateSystemAudioState(); 1370 } 1371 1372 @Override updateSystemAudioState()1373 public void updateSystemAudioState() { 1374 updateInternalCallAudioState(); 1375 setSystemAudioState(mCurrentCallAudioState); 1376 } 1377 1378 @Override isActive()1379 public boolean isActive() { 1380 return true; 1381 } 1382 1383 @Override getRouteCode()1384 public int getRouteCode() { 1385 return CallAudioState.ROUTE_STREAMING; 1386 } 1387 1388 @Override processMessage(Message msg)1389 public boolean processMessage(Message msg) { 1390 if (super.processMessage(msg) == HANDLED) { 1391 return HANDLED; 1392 } 1393 switch (msg.what) { 1394 case SWITCH_EARPIECE: 1395 case USER_SWITCH_EARPIECE: 1396 case SPEAKER_OFF: 1397 // Nothing to do here 1398 return HANDLED; 1399 case SPEAKER_ON: 1400 // fall through 1401 case BT_AUDIO_CONNECTED: 1402 case SWITCH_BLUETOOTH: 1403 case USER_SWITCH_BLUETOOTH: 1404 case SWITCH_HEADSET: 1405 case USER_SWITCH_HEADSET: 1406 case SWITCH_SPEAKER: 1407 case USER_SWITCH_SPEAKER: 1408 return HANDLED; 1409 case SWITCH_FOCUS: 1410 if (msg.arg1 == NO_FOCUS) { 1411 reinitialize(); 1412 mCallAudioManager.notifyAudioOperationsComplete(); 1413 } 1414 return HANDLED; 1415 case STREAMING_FORCE_DISABLED: 1416 reinitialize(); 1417 return HANDLED; 1418 default: 1419 return NOT_HANDLED; 1420 } 1421 } 1422 } 1423 1424 private final BroadcastReceiver mMuteChangeReceiver = new BroadcastReceiver() { 1425 @Override 1426 public void onReceive(Context context, Intent intent) { 1427 Log.startSession("CARSM.mCR"); 1428 try { 1429 if (AudioManager.ACTION_MICROPHONE_MUTE_CHANGED.equals(intent.getAction())) { 1430 if (mCallsManager.isInEmergencyCall()) { 1431 Log.i(this, "Mute was externally changed when there's an emergency call. " + 1432 "Forcing mute back off."); 1433 sendInternalMessage(MUTE_OFF); 1434 } else { 1435 sendInternalMessage(MUTE_EXTERNALLY_CHANGED); 1436 } 1437 } else if (AudioManager.STREAM_MUTE_CHANGED_ACTION.equals(intent.getAction())) { 1438 int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1); 1439 boolean isStreamMuted = intent.getBooleanExtra( 1440 AudioManager.EXTRA_STREAM_VOLUME_MUTED, false); 1441 1442 if (streamType == AudioManager.STREAM_RING && !isStreamMuted) { 1443 Log.i(this, "Ring stream was un-muted."); 1444 mCallAudioManager.onRingerModeChange(); 1445 } 1446 } else { 1447 Log.w(this, "Received non-mute-change intent"); 1448 } 1449 } finally { 1450 Log.endSession(); 1451 } 1452 } 1453 }; 1454 1455 private final BroadcastReceiver mSpeakerPhoneChangeReceiver = new BroadcastReceiver() { 1456 @Override 1457 public void onReceive(Context context, Intent intent) { 1458 Log.startSession("CARSM.mSPCR"); 1459 try { 1460 if (AudioManager.ACTION_SPEAKERPHONE_STATE_CHANGED.equals(intent.getAction())) { 1461 if (mAudioManager != null) { 1462 if (mAudioManager.isSpeakerphoneOn()) { 1463 sendInternalMessage(SPEAKER_ON); 1464 } else { 1465 sendInternalMessage(SPEAKER_OFF); 1466 } 1467 } 1468 } else { 1469 Log.w(this, "Received non-speakerphone-change intent"); 1470 } 1471 } finally { 1472 Log.endSession(); 1473 } 1474 } 1475 }; 1476 1477 private final ActiveEarpieceRoute mActiveEarpieceRoute = new ActiveEarpieceRoute(); 1478 private final ActiveHeadsetRoute mActiveHeadsetRoute = new ActiveHeadsetRoute(); 1479 private final ActiveBluetoothRoute mActiveBluetoothRoute = new ActiveBluetoothRoute(); 1480 private final ActiveSpeakerRoute mActiveSpeakerRoute = new ActiveSpeakerRoute(); 1481 private final RingingBluetoothRoute mRingingBluetoothRoute = new RingingBluetoothRoute(); 1482 private final QuiescentEarpieceRoute mQuiescentEarpieceRoute = new QuiescentEarpieceRoute(); 1483 private final QuiescentHeadsetRoute mQuiescentHeadsetRoute = new QuiescentHeadsetRoute(); 1484 private final QuiescentBluetoothRoute mQuiescentBluetoothRoute = new QuiescentBluetoothRoute(); 1485 private final QuiescentSpeakerRoute mQuiescentSpeakerRoute = new QuiescentSpeakerRoute(); 1486 private final StreamingState mStreamingState = new StreamingState(); 1487 1488 private final Executor mAsyncTaskExecutor; 1489 1490 /** 1491 * A few pieces of hidden state. Used to avoid exponential explosion of number of explicit 1492 * states 1493 */ 1494 private int mDeviceSupportedRoutes; 1495 private int mAvailableRoutes; 1496 private int mAudioFocusType = NO_FOCUS; 1497 private boolean mWasOnSpeaker; 1498 private boolean mIsMuted; 1499 1500 private final Context mContext; 1501 private final CallsManager mCallsManager; 1502 private final AudioManager mAudioManager; 1503 private final BluetoothRouteManager mBluetoothRouteManager; 1504 private final WiredHeadsetManager mWiredHeadsetManager; 1505 private final StatusBarNotifier mStatusBarNotifier; 1506 private final CallAudioManager.AudioServiceFactory mAudioServiceFactory; 1507 private boolean mDoesDeviceSupportEarpieceRoute; 1508 private final TelecomSystem.SyncRoot mLock; 1509 private boolean mHasUserExplicitlyLeftBluetooth = false; 1510 1511 private HashMap<String, Integer> mStateNameToRouteCode; 1512 private HashMap<Integer, AudioState> mRouteCodeToQuiescentState; 1513 1514 // CallAudioState is used as an interface to communicate with many other system components. 1515 // No internal state transitions should depend on this variable. 1516 private CallAudioState mCurrentCallAudioState; 1517 private CallAudioState mLastKnownCallAudioState; 1518 1519 private CallAudioManager mCallAudioManager; 1520 private CallAudioCommunicationDeviceTracker mCommunicationDeviceTracker; 1521 private FeatureFlags mFeatureFlags; 1522 CallAudioRouteStateMachine( Context context, CallsManager callsManager, BluetoothRouteManager bluetoothManager, WiredHeadsetManager wiredHeadsetManager, StatusBarNotifier statusBarNotifier, CallAudioManager.AudioServiceFactory audioServiceFactory, int earpieceControl, Executor asyncTaskExecutor, CallAudioCommunicationDeviceTracker communicationDeviceTracker, FeatureFlags featureFlags)1523 public CallAudioRouteStateMachine( 1524 Context context, 1525 CallsManager callsManager, 1526 BluetoothRouteManager bluetoothManager, 1527 WiredHeadsetManager wiredHeadsetManager, 1528 StatusBarNotifier statusBarNotifier, 1529 CallAudioManager.AudioServiceFactory audioServiceFactory, 1530 int earpieceControl, 1531 Executor asyncTaskExecutor, 1532 CallAudioCommunicationDeviceTracker communicationDeviceTracker, 1533 FeatureFlags featureFlags) { 1534 super(NAME); 1535 mContext = context; 1536 mCallsManager = callsManager; 1537 mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); 1538 mBluetoothRouteManager = bluetoothManager; 1539 mWiredHeadsetManager = wiredHeadsetManager; 1540 mStatusBarNotifier = statusBarNotifier; 1541 mAudioServiceFactory = audioServiceFactory; 1542 mLock = callsManager.getLock(); 1543 mAsyncTaskExecutor = asyncTaskExecutor; 1544 mCommunicationDeviceTracker = communicationDeviceTracker; 1545 mFeatureFlags = featureFlags; 1546 createStates(earpieceControl); 1547 } 1548 1549 /** Used for testing only */ CallAudioRouteStateMachine( Context context, CallsManager callsManager, BluetoothRouteManager bluetoothManager, WiredHeadsetManager wiredHeadsetManager, StatusBarNotifier statusBarNotifier, CallAudioManager.AudioServiceFactory audioServiceFactory, int earpieceControl, Looper looper, Executor asyncTaskExecutor, CallAudioCommunicationDeviceTracker communicationDeviceTracker, FeatureFlags featureFlags)1550 public CallAudioRouteStateMachine( 1551 Context context, 1552 CallsManager callsManager, 1553 BluetoothRouteManager bluetoothManager, 1554 WiredHeadsetManager wiredHeadsetManager, 1555 StatusBarNotifier statusBarNotifier, 1556 CallAudioManager.AudioServiceFactory audioServiceFactory, 1557 int earpieceControl, Looper looper, Executor asyncTaskExecutor, 1558 CallAudioCommunicationDeviceTracker communicationDeviceTracker, 1559 FeatureFlags featureFlags) { 1560 super(NAME, looper); 1561 mContext = context; 1562 mCallsManager = callsManager; 1563 mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); 1564 mBluetoothRouteManager = bluetoothManager; 1565 mWiredHeadsetManager = wiredHeadsetManager; 1566 mStatusBarNotifier = statusBarNotifier; 1567 mAudioServiceFactory = audioServiceFactory; 1568 mLock = callsManager.getLock(); 1569 mAsyncTaskExecutor = asyncTaskExecutor; 1570 mCommunicationDeviceTracker = communicationDeviceTracker; 1571 mFeatureFlags = featureFlags; 1572 createStates(earpieceControl); 1573 } 1574 createStates(int earpieceControl)1575 private void createStates(int earpieceControl) { 1576 switch (earpieceControl) { 1577 case EARPIECE_FORCE_DISABLED: 1578 mDoesDeviceSupportEarpieceRoute = false; 1579 break; 1580 case EARPIECE_FORCE_ENABLED: 1581 mDoesDeviceSupportEarpieceRoute = true; 1582 break; 1583 default: 1584 mDoesDeviceSupportEarpieceRoute = checkForEarpieceSupport(); 1585 } 1586 1587 addState(mActiveEarpieceRoute); 1588 addState(mActiveHeadsetRoute); 1589 addState(mActiveBluetoothRoute); 1590 addState(mActiveSpeakerRoute); 1591 addState(mRingingBluetoothRoute); 1592 addState(mQuiescentEarpieceRoute); 1593 addState(mQuiescentHeadsetRoute); 1594 addState(mQuiescentBluetoothRoute); 1595 addState(mQuiescentSpeakerRoute); 1596 addState(mStreamingState); 1597 1598 1599 mStateNameToRouteCode = new HashMap<>(8); 1600 mStateNameToRouteCode.put(mQuiescentEarpieceRoute.getName(), ROUTE_EARPIECE); 1601 mStateNameToRouteCode.put(mQuiescentBluetoothRoute.getName(), ROUTE_BLUETOOTH); 1602 mStateNameToRouteCode.put(mQuiescentHeadsetRoute.getName(), ROUTE_WIRED_HEADSET); 1603 mStateNameToRouteCode.put(mQuiescentSpeakerRoute.getName(), ROUTE_SPEAKER); 1604 mStateNameToRouteCode.put(mRingingBluetoothRoute.getName(), ROUTE_BLUETOOTH); 1605 mStateNameToRouteCode.put(mActiveEarpieceRoute.getName(), ROUTE_EARPIECE); 1606 mStateNameToRouteCode.put(mActiveBluetoothRoute.getName(), ROUTE_BLUETOOTH); 1607 mStateNameToRouteCode.put(mActiveHeadsetRoute.getName(), ROUTE_WIRED_HEADSET); 1608 mStateNameToRouteCode.put(mActiveSpeakerRoute.getName(), ROUTE_SPEAKER); 1609 mStateNameToRouteCode.put(mStreamingState.getName(), ROUTE_STREAMING); 1610 1611 mRouteCodeToQuiescentState = new HashMap<>(4); 1612 mRouteCodeToQuiescentState.put(ROUTE_EARPIECE, mQuiescentEarpieceRoute); 1613 mRouteCodeToQuiescentState.put(ROUTE_BLUETOOTH, mQuiescentBluetoothRoute); 1614 mRouteCodeToQuiescentState.put(ROUTE_SPEAKER, mQuiescentSpeakerRoute); 1615 mRouteCodeToQuiescentState.put(ROUTE_WIRED_HEADSET, mQuiescentHeadsetRoute); 1616 mRouteCodeToQuiescentState.put(ROUTE_STREAMING, mStreamingState); 1617 } 1618 setCallAudioManager(CallAudioManager callAudioManager)1619 public void setCallAudioManager(CallAudioManager callAudioManager) { 1620 mCallAudioManager = callAudioManager; 1621 } 1622 1623 /** 1624 * Initializes the state machine with info on initial audio route, supported audio routes, 1625 * and mute status. 1626 */ initialize()1627 public void initialize() { 1628 CallAudioState initState = getInitialAudioState(); 1629 initialize(initState); 1630 } 1631 initialize(CallAudioState initState)1632 public void initialize(CallAudioState initState) { 1633 if ((initState.getRoute() & getCurrentCallSupportedRoutes()) == 0) { 1634 Log.e(this, new IllegalArgumentException(), "Route %d specified when supported call" + 1635 " routes are: %d", initState.getRoute(), getCurrentCallSupportedRoutes()); 1636 } 1637 1638 mCurrentCallAudioState = initState; 1639 mLastKnownCallAudioState = initState; 1640 mDeviceSupportedRoutes = initState.getSupportedRouteMask(); 1641 mAvailableRoutes = mDeviceSupportedRoutes & getCurrentCallSupportedRoutes(); 1642 mIsMuted = initState.isMuted(); 1643 mWasOnSpeaker = false; 1644 IntentFilter micMuteChangedFilter = new IntentFilter( 1645 AudioManager.ACTION_MICROPHONE_MUTE_CHANGED); 1646 micMuteChangedFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); 1647 mContext.registerReceiver(mMuteChangeReceiver, micMuteChangedFilter); 1648 1649 IntentFilter muteChangedFilter = new IntentFilter(AudioManager.STREAM_MUTE_CHANGED_ACTION); 1650 muteChangedFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); 1651 mContext.registerReceiver(mMuteChangeReceiver, muteChangedFilter); 1652 1653 IntentFilter speakerChangedFilter = new IntentFilter( 1654 AudioManager.ACTION_SPEAKERPHONE_STATE_CHANGED); 1655 speakerChangedFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); 1656 mContext.registerReceiver(mSpeakerPhoneChangeReceiver, speakerChangedFilter); 1657 1658 mStatusBarNotifier.notifyMute(initState.isMuted()); 1659 // We used to call mStatusBarNotifier.notifySpeakerphone, but that makes no sense as there 1660 // is never a call at this boot (init) time. 1661 setInitialState(mRouteCodeToQuiescentState.get(initState.getRoute())); 1662 start(); 1663 } 1664 1665 /** 1666 * Getter for the current CallAudioState object that the state machine is keeping track of. 1667 * Used for compatibility purposes. 1668 */ getCurrentCallAudioState()1669 public CallAudioState getCurrentCallAudioState() { 1670 return mCurrentCallAudioState; 1671 } 1672 sendMessageWithSessionInfo(int message, int arg)1673 public void sendMessageWithSessionInfo(int message, int arg) { 1674 sendMessageWithSessionInfo(message, arg, (String) null); 1675 } 1676 sendMessageWithSessionInfo(int message)1677 public void sendMessageWithSessionInfo(int message) { 1678 sendMessageWithSessionInfo(message, 0, (String) null); 1679 } 1680 sendMessageWithSessionInfo(int message, int arg, String data)1681 public void sendMessageWithSessionInfo(int message, int arg, String data) { 1682 SomeArgs args = SomeArgs.obtain(); 1683 args.arg1 = Log.createSubsession(); 1684 args.arg2 = data; 1685 sendMessage(message, arg, 0, args); 1686 } 1687 sendMessageWithSessionInfo(int message, int arg, BluetoothDevice bluetoothDevice)1688 public void sendMessageWithSessionInfo(int message, int arg, BluetoothDevice bluetoothDevice) { 1689 // ignore, only used in CallAudioRouteController 1690 } 1691 1692 @Override sendMessage(int message, Runnable r)1693 public void sendMessage(int message, Runnable r) { 1694 super.sendMessage(message, r); 1695 } 1696 1697 /** 1698 * This is for state-independent changes in audio route (i.e. muting or runnables) 1699 * @param msg that couldn't be handled. 1700 */ 1701 @Override unhandledMessage(Message msg)1702 protected void unhandledMessage(Message msg) { 1703 switch (msg.what) { 1704 case MUTE_ON: 1705 setMuteOn(true); 1706 updateSystemMuteState(); 1707 return; 1708 case MUTE_OFF: 1709 setMuteOn(false); 1710 updateSystemMuteState(); 1711 return; 1712 case MUTE_EXTERNALLY_CHANGED: 1713 mIsMuted = mAudioManager.isMicrophoneMute(); 1714 if (isInActiveState()) { 1715 updateSystemMuteState(); 1716 } 1717 return; 1718 case TOGGLE_MUTE: 1719 if (mIsMuted) { 1720 sendInternalMessage(MUTE_OFF); 1721 } else { 1722 sendInternalMessage(MUTE_ON); 1723 } 1724 return; 1725 case UPDATE_SYSTEM_AUDIO_ROUTE: 1726 if (mFeatureFlags.availableRoutesNeverUpdatedAfterSetSystemAudioState()) { 1727 // Ensure available routes is updated. 1728 updateRouteForForegroundCall(); 1729 // Ensure current audio state gets updated to take this into account. 1730 updateInternalCallAudioState(); 1731 // Either resend the current audio state as it stands, or update to reflect any 1732 // changes put into place based on mAvailableRoutes 1733 setSystemAudioState(mCurrentCallAudioState, true); 1734 } else { 1735 updateInternalCallAudioState(); 1736 updateRouteForForegroundCall(); 1737 resendSystemAudioState(); 1738 } 1739 return; 1740 case RUN_RUNNABLE: 1741 java.lang.Runnable r = (java.lang.Runnable) msg.obj; 1742 r.run(); 1743 return; 1744 default: 1745 Log.e(this, new IllegalStateException(), "Unexpected message code %d", msg.what); 1746 } 1747 } 1748 quitStateMachine()1749 public void quitStateMachine() { 1750 quitNow(); 1751 } 1752 dump(IndentingPrintWriter pw)1753 public void dump(IndentingPrintWriter pw) { 1754 pw.print("Current state: "); 1755 pw.println(getCurrentState().getName()); 1756 pw.println("Pending messages:"); 1757 pw.increaseIndent(); 1758 dumpPendingMessages(pw); 1759 pw.decreaseIndent(); 1760 } 1761 dumpPendingMessages(IndentingPrintWriter pw)1762 public void dumpPendingMessages(IndentingPrintWriter pw) { 1763 getAdapterHandler().getLooper().dump(pw::println, ""); 1764 } 1765 isHfpDeviceAvailable()1766 public boolean isHfpDeviceAvailable() { 1767 return mBluetoothRouteManager.isBluetoothAvailable(); 1768 } 1769 setSpeakerphoneOn(boolean on)1770 private void setSpeakerphoneOn(boolean on) { 1771 Log.i(this, "turning speaker phone %s", on); 1772 final boolean hasAnyCalls = mCallsManager.hasAnyCalls(); 1773 // These APIs are all via two-way binder calls so can potentially block Telecom. Since none 1774 // of this has to happen in the Telecom lock we'll offload it to the async executor. 1775 boolean speakerOn = false; 1776 if (mFeatureFlags.callAudioCommunicationDeviceRefactor()) { 1777 if (on) { 1778 speakerOn = mCommunicationDeviceTracker.setCommunicationDevice( 1779 AudioDeviceInfo.TYPE_BUILTIN_SPEAKER, null); 1780 } else { 1781 mCommunicationDeviceTracker.clearCommunicationDevice( 1782 AudioDeviceInfo.TYPE_BUILTIN_SPEAKER); 1783 } 1784 } else { 1785 speakerOn = processLegacySpeakerCommunicationDevice(on); 1786 } 1787 mStatusBarNotifier.notifySpeakerphone(hasAnyCalls && speakerOn); 1788 } 1789 setBluetoothOn(String address)1790 private void setBluetoothOn(String address) { 1791 if (mBluetoothRouteManager.isBluetoothAvailable()) { 1792 BluetoothDevice connectedDevice = 1793 mBluetoothRouteManager.getBluetoothAudioConnectedDevice(); 1794 if (address == null && connectedDevice != null) { 1795 // null means connect to any device, so if we're already connected to some device, 1796 // that means we can just tell ourselves that it's connected. 1797 // Do still try to connect audio though, so that BluetoothRouteManager knows that 1798 // there's an active call. 1799 Log.i(this, "Bluetooth audio already on."); 1800 sendInternalMessage(BT_AUDIO_CONNECTED); 1801 mBluetoothRouteManager.connectBluetoothAudio(connectedDevice.getAddress()); 1802 return; 1803 } 1804 if (connectedDevice == null || !Objects.equals(address, connectedDevice.getAddress())) { 1805 Log.i(this, "connecting bluetooth audio: %s", address); 1806 mBluetoothRouteManager.connectBluetoothAudio(address); 1807 } 1808 } 1809 } 1810 setBluetoothOff()1811 private void setBluetoothOff() { 1812 if (mBluetoothRouteManager.isBluetoothAvailable()) { 1813 if (mBluetoothRouteManager.isBluetoothAudioConnectedOrPending()) { 1814 Log.i(this, "disconnecting bluetooth audio"); 1815 mBluetoothRouteManager.disconnectBluetoothAudio(); 1816 } 1817 } 1818 } 1819 setMuteOn(boolean mute)1820 private void setMuteOn(boolean mute) { 1821 mIsMuted = mute; 1822 Log.addEvent(mCallsManager.getForegroundCall(), mute ? 1823 LogUtils.Events.MUTE : LogUtils.Events.UNMUTE); 1824 if (mute != mAudioManager.isMicrophoneMute() && isInActiveState()) { 1825 IAudioService audio = mAudioServiceFactory.getAudioService(); 1826 Log.i(this, "changing microphone mute state to: %b [serviceIsNull=%b]", 1827 mute, audio == null); 1828 if (audio != null) { 1829 try { 1830 // We use the audio service directly here so that we can specify 1831 // the current user. Telecom runs in the system_server process which 1832 // may run as a separate user from the foreground user. If we 1833 // used AudioManager directly, we would change mute for the system's 1834 // user and not the current foreground, which we want to avoid. 1835 audio.setMicrophoneMute(mute, mContext.getOpPackageName(), 1836 getCurrentUserId(), mContext.getAttributionTag()); 1837 } catch (RemoteException e) { 1838 Log.e(this, e, "Remote exception while toggling mute."); 1839 } 1840 // TODO: Check microphone state after attempting to set to ensure that 1841 // our state corroborates AudioManager's state. 1842 } 1843 } 1844 } 1845 updateSystemMuteState()1846 private void updateSystemMuteState() { 1847 CallAudioState newCallAudioState = new CallAudioState(mIsMuted, 1848 mCurrentCallAudioState.getRoute(), 1849 mAvailableRoutes, 1850 mCurrentCallAudioState.getActiveBluetoothDevice(), 1851 mBluetoothRouteManager.getConnectedDevices()); 1852 setSystemAudioState(newCallAudioState); 1853 updateInternalCallAudioState(); 1854 } 1855 1856 /** 1857 * Updates the CallAudioState object from current internal state. The result is used for 1858 * external communication only. 1859 */ updateInternalCallAudioState()1860 private void updateInternalCallAudioState() { 1861 IState currentState = getCurrentState(); 1862 if (currentState == null) { 1863 Log.e(this, new IllegalStateException(), "Current state should never be null" + 1864 " when updateInternalCallAudioState is called."); 1865 mCurrentCallAudioState = new CallAudioState( 1866 mIsMuted, mCurrentCallAudioState.getRoute(), mAvailableRoutes, 1867 mBluetoothRouteManager.getBluetoothAudioConnectedDevice(), 1868 mBluetoothRouteManager.getConnectedDevices()); 1869 return; 1870 } 1871 int currentRoute = mStateNameToRouteCode.get(currentState.getName()); 1872 mCurrentCallAudioState = new CallAudioState(mIsMuted, currentRoute, mAvailableRoutes, 1873 mBluetoothRouteManager.getBluetoothAudioConnectedDevice(), 1874 mBluetoothRouteManager.getConnectedDevices()); 1875 } 1876 setSystemAudioState(CallAudioState newCallAudioState)1877 private void setSystemAudioState(CallAudioState newCallAudioState) { 1878 setSystemAudioState(newCallAudioState, false); 1879 } 1880 resendSystemAudioState()1881 private void resendSystemAudioState() { 1882 setSystemAudioState(mLastKnownCallAudioState, true); 1883 } 1884 1885 @VisibleForTesting getLastKnownCallAudioState()1886 public CallAudioState getLastKnownCallAudioState() { 1887 return mLastKnownCallAudioState; 1888 } 1889 setSystemAudioState(CallAudioState newCallAudioState, boolean force)1890 private void setSystemAudioState(CallAudioState newCallAudioState, boolean force) { 1891 synchronized (mLock) { 1892 Log.i(this, "setSystemAudioState: changing from %s to %s", mLastKnownCallAudioState, 1893 newCallAudioState); 1894 if (force || !newCallAudioState.equals(mLastKnownCallAudioState)) { 1895 mStatusBarNotifier.notifyMute(newCallAudioState.isMuted()); 1896 mCallsManager.onCallAudioStateChanged(mLastKnownCallAudioState, newCallAudioState); 1897 updateAudioStateForTrackedCalls(newCallAudioState); 1898 mLastKnownCallAudioState = newCallAudioState; 1899 } 1900 } 1901 } 1902 updateAudioStateForTrackedCalls(CallAudioState newCallAudioState)1903 private void updateAudioStateForTrackedCalls(CallAudioState newCallAudioState) { 1904 Set<Call> calls = mCallsManager.getTrackedCalls(); 1905 for (Call call : calls) { 1906 if (call != null && call.getConnectionService() != null) { 1907 call.getConnectionService().onCallAudioStateChanged(call, newCallAudioState); 1908 } 1909 } 1910 } 1911 calculateSupportedRoutes()1912 private int calculateSupportedRoutes() { 1913 int routeMask = CallAudioState.ROUTE_SPEAKER; 1914 1915 if (mWiredHeadsetManager.isPluggedIn()) { 1916 routeMask |= CallAudioState.ROUTE_WIRED_HEADSET; 1917 } else if (mDoesDeviceSupportEarpieceRoute){ 1918 routeMask |= CallAudioState.ROUTE_EARPIECE; 1919 } 1920 1921 if (mBluetoothRouteManager.isBluetoothAvailable()) { 1922 routeMask |= CallAudioState.ROUTE_BLUETOOTH; 1923 } 1924 1925 return routeMask; 1926 } 1927 sendInternalMessage(int messageCode)1928 private void sendInternalMessage(int messageCode) { 1929 sendInternalMessage(messageCode, 0); 1930 } 1931 sendInternalMessage(int messageCode, int arg1)1932 private void sendInternalMessage(int messageCode, int arg1) { 1933 // Internal messages are messages which the state machine sends to itself in the 1934 // course of processing externally-sourced messages. We want to send these messages at 1935 // the front of the queue in order to make actions appear atomic to the user and to 1936 // prevent scenarios such as these: 1937 // 1. State machine handler thread is suspended for some reason. 1938 // 2. Headset gets connected (sends CONNECT_HEADSET). 1939 // 3. User switches to speakerphone in the UI (sends SWITCH_SPEAKER). 1940 // 4. State machine handler is un-suspended. 1941 // 5. State machine handler processes the CONNECT_HEADSET message and sends 1942 // SWITCH_HEADSET at end of queue. 1943 // 6. State machine handler processes SWITCH_SPEAKER. 1944 // 7. State machine handler processes SWITCH_HEADSET. 1945 Session subsession = Log.createSubsession(); 1946 if(subsession != null) { 1947 SomeArgs args = SomeArgs.obtain(); 1948 args.arg1 = subsession; 1949 sendMessageAtFrontOfQueue(messageCode, arg1, 0, args); 1950 } else { 1951 sendMessageAtFrontOfQueue(messageCode, arg1); 1952 } 1953 } 1954 getInitialAudioState()1955 private CallAudioState getInitialAudioState() { 1956 int supportedRouteMask = calculateSupportedRoutes() & getCurrentCallSupportedRoutes(); 1957 final int route; 1958 1959 if ((supportedRouteMask & ROUTE_BLUETOOTH) != 0 1960 && mBluetoothRouteManager.hasBtActiveDevice()) { 1961 route = ROUTE_BLUETOOTH; 1962 } else if ((supportedRouteMask & ROUTE_WIRED_HEADSET) != 0) { 1963 route = ROUTE_WIRED_HEADSET; 1964 } else if ((supportedRouteMask & ROUTE_EARPIECE) != 0) { 1965 route = ROUTE_EARPIECE; 1966 } else { 1967 route = ROUTE_SPEAKER; 1968 } 1969 1970 return new CallAudioState(false, route, supportedRouteMask, null, 1971 mBluetoothRouteManager.getConnectedDevices()); 1972 } 1973 getCurrentUserId()1974 private int getCurrentUserId() { 1975 final long ident = Binder.clearCallingIdentity(); 1976 try { 1977 UserInfo currentUser = ActivityManager.getService().getCurrentUser(); 1978 return currentUser.id; 1979 } catch (RemoteException e) { 1980 // Activity manager not running, nothing we can do assume user 0. 1981 } finally { 1982 Binder.restoreCallingIdentity(ident); 1983 } 1984 return UserHandle.USER_OWNER; 1985 } 1986 isInActiveState()1987 public boolean isInActiveState() { 1988 AudioState currentState = (AudioState) getCurrentState(); 1989 if (currentState == null) { 1990 Log.w(this, "Current state is null, assuming inactive state"); 1991 return false; 1992 } 1993 return currentState.isActive(); 1994 } 1995 checkForEarpieceSupport()1996 private boolean checkForEarpieceSupport() { 1997 AudioDeviceInfo[] deviceList = mAudioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS); 1998 for (AudioDeviceInfo device: deviceList) { 1999 if (device.getType() == AudioDeviceInfo.TYPE_BUILTIN_EARPIECE) { 2000 return true; 2001 } 2002 } 2003 // No earpiece found 2004 return false; 2005 } 2006 isWatchActiveOrOnlyWatchesAvailable()2007 private boolean isWatchActiveOrOnlyWatchesAvailable() { 2008 if (!mFeatureFlags.ignoreAutoRouteToWatchDevice()) { 2009 Log.i(this, "isWatchActiveOrOnlyWatchesAvailable: Flag is disabled."); 2010 return false; 2011 } 2012 2013 boolean containsWatchDevice = false; 2014 boolean containsNonWatchDevice = false; 2015 Collection<BluetoothDevice> connectedBtDevices = 2016 mBluetoothRouteManager.getConnectedDevices(); 2017 2018 for (BluetoothDevice connectedDevice: connectedBtDevices) { 2019 if (mBluetoothRouteManager.isWatch(connectedDevice)) { 2020 containsWatchDevice = true; 2021 } else { 2022 containsNonWatchDevice = true; 2023 } 2024 } 2025 2026 // Don't ignore switch if watch is already the active device. 2027 boolean isActiveDeviceWatch = mBluetoothRouteManager.isWatch( 2028 mBluetoothRouteManager.getBluetoothAudioConnectedDevice()); 2029 Log.i(this, "isWatchActiveOrOnlyWatchesAvailable: contains watch: %s, contains " 2030 + "non-wearable device: %s, is active device a watch: %s.", 2031 containsWatchDevice, containsNonWatchDevice, isActiveDeviceWatch); 2032 return containsWatchDevice && !containsNonWatchDevice && !isActiveDeviceWatch; 2033 } 2034 processLegacySpeakerCommunicationDevice(boolean on)2035 private boolean processLegacySpeakerCommunicationDevice(boolean on) { 2036 AudioDeviceInfo speakerDevice = null; 2037 for (AudioDeviceInfo info : mAudioManager.getAvailableCommunicationDevices()) { 2038 if (info.getType() == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER) { 2039 speakerDevice = info; 2040 break; 2041 } 2042 } 2043 boolean speakerOn = false; 2044 if (speakerDevice != null && on) { 2045 boolean result = mAudioManager.setCommunicationDevice(speakerDevice); 2046 if (result) { 2047 speakerOn = true; 2048 } 2049 } else { 2050 AudioDeviceInfo curDevice = mAudioManager.getCommunicationDevice(); 2051 if (curDevice != null 2052 && curDevice.getType() == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER) { 2053 mAudioManager.clearCommunicationDevice(); 2054 } 2055 } 2056 return speakerOn; 2057 } 2058 calculateBaselineRouteMessage(boolean isExplicitUserRequest, boolean includeBluetooth)2059 private int calculateBaselineRouteMessage(boolean isExplicitUserRequest, 2060 boolean includeBluetooth) { 2061 boolean isSkipEarpiece = false; 2062 if (!isExplicitUserRequest) { 2063 synchronized (mLock) { 2064 // Check video calls to skip earpiece since the baseline for video 2065 // calls should be the speakerphone route 2066 isSkipEarpiece = mCallsManager.hasVideoCall(); 2067 } 2068 } 2069 if ((mAvailableRoutes & ROUTE_BLUETOOTH) != 0 2070 && !mHasUserExplicitlyLeftBluetooth 2071 && includeBluetooth && !isWatchActiveOrOnlyWatchesAvailable()) { 2072 return isExplicitUserRequest ? USER_SWITCH_BLUETOOTH : SWITCH_BLUETOOTH; 2073 } else if ((mAvailableRoutes & ROUTE_EARPIECE) != 0 && !isSkipEarpiece) { 2074 return isExplicitUserRequest ? USER_SWITCH_EARPIECE : SWITCH_EARPIECE; 2075 } else if ((mAvailableRoutes & ROUTE_WIRED_HEADSET) != 0) { 2076 return isExplicitUserRequest ? USER_SWITCH_HEADSET : SWITCH_HEADSET; 2077 } else { 2078 return isExplicitUserRequest ? USER_SWITCH_SPEAKER : SWITCH_SPEAKER; 2079 } 2080 } 2081 reinitialize()2082 private void reinitialize() { 2083 CallAudioState initState = getInitialAudioState(); 2084 mDeviceSupportedRoutes = initState.getSupportedRouteMask(); 2085 mAvailableRoutes = mDeviceSupportedRoutes & getCurrentCallSupportedRoutes(); 2086 mIsMuted = initState.isMuted(); 2087 setSpeakerphoneOn(initState.getRoute() == CallAudioState.ROUTE_SPEAKER); 2088 setMuteOn(mIsMuted); 2089 mWasOnSpeaker = false; 2090 mHasUserExplicitlyLeftBluetooth = false; 2091 mLastKnownCallAudioState = initState; 2092 transitionTo(mRouteCodeToQuiescentState.get(initState.getRoute())); 2093 } 2094 updateRouteForForegroundCall()2095 private void updateRouteForForegroundCall() { 2096 mAvailableRoutes = mDeviceSupportedRoutes & getCurrentCallSupportedRoutes(); 2097 2098 CallAudioState currentState = getCurrentCallAudioState(); 2099 2100 // Move to baseline route in the case the current route is no longer available. 2101 if ((mAvailableRoutes & currentState.getRoute()) == 0) { 2102 sendInternalMessage(calculateBaselineRouteMessage(false, true)); 2103 } 2104 } 2105 getCurrentCallSupportedRoutes()2106 private int getCurrentCallSupportedRoutes() { 2107 int supportedRoutes = CallAudioState.ROUTE_ALL; 2108 2109 if (mCallsManager.getForegroundCall() != null) { 2110 supportedRoutes &= mCallsManager.getForegroundCall().getSupportedAudioRoutes(); 2111 } 2112 2113 return supportedRoutes; 2114 } 2115 modifyRoutes(int base, int remove, int add, boolean considerCurrentCall)2116 private int modifyRoutes(int base, int remove, int add, boolean considerCurrentCall) { 2117 base &= ~remove; 2118 2119 if (considerCurrentCall) { 2120 add &= getCurrentCallSupportedRoutes(); 2121 } 2122 2123 base |= add; 2124 2125 return base; 2126 } 2127 2128 @Override getAdapterHandler()2129 public Handler getAdapterHandler() { 2130 return getHandler(); 2131 } 2132 2133 @Override getPendingAudioRoute()2134 public PendingAudioRoute getPendingAudioRoute() { 2135 // Only used by CallAudioRouteController. 2136 return null; 2137 } 2138 } 2139