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 17package com.android.server.telecom; 18 19 20import android.app.ActivityManager; 21import android.content.Context; 22import android.content.pm.UserInfo; 23import android.media.AudioManager; 24import android.media.IAudioService; 25import android.os.Binder; 26import android.os.Message; 27import android.os.RemoteException; 28import android.os.SystemProperties; 29import android.os.UserHandle; 30import android.telecom.CallAudioState; 31import android.telecom.Log; 32import android.telecom.Logging.Session; 33import android.util.SparseArray; 34 35import com.android.internal.util.IState; 36import com.android.internal.util.IndentingPrintWriter; 37import com.android.internal.util.State; 38import com.android.internal.util.StateMachine; 39import com.android.server.telecom.bluetooth.BluetoothRouteManager; 40 41import java.util.HashMap; 42 43/** 44 * This class describes the available routes of a call as a state machine. 45 * Transitions are caused solely by the commands sent as messages. Possible values for msg.what 46 * are defined as event constants in this file. 47 * 48 * The eight states are all instances of the abstract base class, {@link AudioState}. Each state 49 * is a combination of one of the four audio routes (earpiece, wired headset, bluetooth, and 50 * speakerphone) and audio focus status (active or quiescent). 51 * 52 * Messages are processed first by the processMessage method in the base class, AudioState. 53 * Any messages not completely handled by AudioState are further processed by the same method in 54 * the route-specific abstract classes: {@link EarpieceRoute}, {@link HeadsetRoute}, 55 * {@link BluetoothRoute}, and {@link SpeakerRoute}. Finally, messages that are not handled at 56 * this level are then processed by the classes corresponding to the state instances themselves. 57 * 58 * There are several variables carrying additional state. These include: 59 * mAvailableRoutes: A bitmask describing which audio routes are available 60 * mWasOnSpeaker: A boolean indicating whether we should switch to speakerphone after disconnecting 61 * from a wired headset 62 * mIsMuted: a boolean indicating whether the audio is muted 63 */ 64public class CallAudioRouteStateMachine extends StateMachine { 65 private static final String TELECOM_PACKAGE = 66 CallAudioRouteStateMachine.class.getPackage().getName(); 67 68 /** Direct the audio stream through the device's earpiece. */ 69 public static final int ROUTE_EARPIECE = CallAudioState.ROUTE_EARPIECE; 70 71 /** Direct the audio stream through Bluetooth. */ 72 public static final int ROUTE_BLUETOOTH = CallAudioState.ROUTE_BLUETOOTH; 73 74 /** Direct the audio stream through a wired headset. */ 75 public static final int ROUTE_WIRED_HEADSET = CallAudioState.ROUTE_WIRED_HEADSET; 76 77 /** Direct the audio stream through the device's speakerphone. */ 78 public static final int ROUTE_SPEAKER = CallAudioState.ROUTE_SPEAKER; 79 80 /** Valid values for msg.what */ 81 public static final int CONNECT_WIRED_HEADSET = 1; 82 public static final int DISCONNECT_WIRED_HEADSET = 2; 83 public static final int CONNECT_BLUETOOTH = 3; 84 public static final int DISCONNECT_BLUETOOTH = 4; 85 public static final int CONNECT_DOCK = 5; 86 public static final int DISCONNECT_DOCK = 6; 87 88 public static final int SWITCH_EARPIECE = 1001; 89 public static final int SWITCH_BLUETOOTH = 1002; 90 public static final int SWITCH_HEADSET = 1003; 91 public static final int SWITCH_SPEAKER = 1004; 92 // Wired headset, earpiece, or speakerphone, in that order of precedence. 93 public static final int SWITCH_BASELINE_ROUTE = 1005; 94 public static final int BT_AUDIO_DISCONNECT = 1006; 95 96 public static final int USER_SWITCH_EARPIECE = 1101; 97 public static final int USER_SWITCH_BLUETOOTH = 1102; 98 public static final int USER_SWITCH_HEADSET = 1103; 99 public static final int USER_SWITCH_SPEAKER = 1104; 100 public static final int USER_SWITCH_BASELINE_ROUTE = 1105; 101 102 public static final int UPDATE_SYSTEM_AUDIO_ROUTE = 1201; 103 104 public static final int MUTE_ON = 3001; 105 public static final int MUTE_OFF = 3002; 106 public static final int TOGGLE_MUTE = 3003; 107 108 public static final int SWITCH_FOCUS = 4001; 109 110 // Used in testing to execute verifications. Not compatible with subsessions. 111 public static final int RUN_RUNNABLE = 9001; 112 113 /** Valid values for mAudioFocusType */ 114 public static final int NO_FOCUS = 1; 115 public static final int ACTIVE_FOCUS = 2; 116 public static final int RINGING_FOCUS = 3; 117 118 private static final SparseArray<String> AUDIO_ROUTE_TO_LOG_EVENT = new SparseArray<String>() {{ 119 put(CallAudioState.ROUTE_BLUETOOTH, LogUtils.Events.AUDIO_ROUTE_BT); 120 put(CallAudioState.ROUTE_EARPIECE, LogUtils.Events.AUDIO_ROUTE_EARPIECE); 121 put(CallAudioState.ROUTE_SPEAKER, LogUtils.Events.AUDIO_ROUTE_SPEAKER); 122 put(CallAudioState.ROUTE_WIRED_HEADSET, LogUtils.Events.AUDIO_ROUTE_HEADSET); 123 }}; 124 125 private static final SparseArray<String> MESSAGE_CODE_TO_NAME = new SparseArray<String>() {{ 126 put(CONNECT_WIRED_HEADSET, "CONNECT_WIRED_HEADSET"); 127 put(DISCONNECT_WIRED_HEADSET, "DISCONNECT_WIRED_HEADSET"); 128 put(CONNECT_BLUETOOTH, "CONNECT_BLUETOOTH"); 129 put(DISCONNECT_BLUETOOTH, "DISCONNECT_BLUETOOTH"); 130 put(CONNECT_DOCK, "CONNECT_DOCK"); 131 put(DISCONNECT_DOCK, "DISCONNECT_DOCK"); 132 133 put(SWITCH_EARPIECE, "SWITCH_EARPIECE"); 134 put(SWITCH_BLUETOOTH, "SWITCH_BLUETOOTH"); 135 put(SWITCH_HEADSET, "SWITCH_HEADSET"); 136 put(SWITCH_SPEAKER, "SWITCH_SPEAKER"); 137 put(SWITCH_BASELINE_ROUTE, "SWITCH_BASELINE_ROUTE"); 138 put(BT_AUDIO_DISCONNECT, "BT_AUDIO_DISCONNECT"); 139 140 put(USER_SWITCH_EARPIECE, "USER_SWITCH_EARPIECE"); 141 put(USER_SWITCH_BLUETOOTH, "USER_SWITCH_BLUETOOTH"); 142 put(USER_SWITCH_HEADSET, "USER_SWITCH_HEADSET"); 143 put(USER_SWITCH_SPEAKER, "USER_SWITCH_SPEAKER"); 144 put(USER_SWITCH_BASELINE_ROUTE, "USER_SWITCH_BASELINE_ROUTE"); 145 146 put(UPDATE_SYSTEM_AUDIO_ROUTE, "UPDATE_SYSTEM_AUDIO_ROUTE"); 147 148 put(MUTE_ON, "MUTE_ON"); 149 put(MUTE_OFF, "MUTE_OFF"); 150 put(TOGGLE_MUTE, "TOGGLE_MUTE"); 151 152 put(SWITCH_FOCUS, "SWITCH_FOCUS"); 153 154 put(RUN_RUNNABLE, "RUN_RUNNABLE"); 155 }}; 156 157 private static final String ACTIVE_EARPIECE_ROUTE_NAME = "ActiveEarpieceRoute"; 158 private static final String ACTIVE_BLUETOOTH_ROUTE_NAME = "ActiveBluetoothRoute"; 159 private static final String ACTIVE_SPEAKER_ROUTE_NAME = "ActiveSpeakerRoute"; 160 private static final String ACTIVE_HEADSET_ROUTE_NAME = "ActiveHeadsetRoute"; 161 private static final String RINGING_BLUETOOTH_ROUTE_NAME = "RingingBluetoothRoute"; 162 private static final String QUIESCENT_EARPIECE_ROUTE_NAME = "QuiescentEarpieceRoute"; 163 private static final String QUIESCENT_BLUETOOTH_ROUTE_NAME = "QuiescentBluetoothRoute"; 164 private static final String QUIESCENT_SPEAKER_ROUTE_NAME = "QuiescentSpeakerRoute"; 165 private static final String QUIESCENT_HEADSET_ROUTE_NAME = "QuiescentHeadsetRoute"; 166 167 public static final String NAME = CallAudioRouteStateMachine.class.getName(); 168 169 @Override 170 protected void onPreHandleMessage(Message msg) { 171 if (msg.obj != null && msg.obj instanceof Session) { 172 String messageCodeName = MESSAGE_CODE_TO_NAME.get(msg.what, "unknown"); 173 Log.continueSession((Session) msg.obj, "CARSM.pM_" + messageCodeName); 174 Log.i(this, "Message received: %s=%d, arg1=%d", messageCodeName, msg.what, msg.arg1); 175 } 176 } 177 178 @Override 179 protected void onPostHandleMessage(Message msg) { 180 Log.endSession(); 181 } 182 183 abstract class AudioState extends State { 184 @Override 185 public void enter() { 186 super.enter(); 187 Log.addEvent(mCallsManager.getForegroundCall(), LogUtils.Events.AUDIO_ROUTE, 188 "Entering state " + getName()); 189 } 190 191 @Override 192 public void exit() { 193 Log.addEvent(mCallsManager.getForegroundCall(), LogUtils.Events.AUDIO_ROUTE, 194 "Leaving state " + getName()); 195 super.exit(); 196 } 197 198 @Override 199 public boolean processMessage(Message msg) { 200 int addedRoutes = 0; 201 int removedRoutes = 0; 202 203 switch (msg.what) { 204 case CONNECT_WIRED_HEADSET: 205 Log.addEvent(mCallsManager.getForegroundCall(), LogUtils.Events.AUDIO_ROUTE, 206 "Wired headset connected"); 207 removedRoutes |= ROUTE_EARPIECE; 208 addedRoutes |= ROUTE_WIRED_HEADSET; 209 break; 210 case CONNECT_BLUETOOTH: 211 Log.addEvent(mCallsManager.getForegroundCall(), LogUtils.Events.AUDIO_ROUTE, 212 "Bluetooth connected"); 213 addedRoutes |= ROUTE_BLUETOOTH; 214 break; 215 case DISCONNECT_WIRED_HEADSET: 216 Log.addEvent(mCallsManager.getForegroundCall(), LogUtils.Events.AUDIO_ROUTE, 217 "Wired headset disconnected"); 218 removedRoutes |= ROUTE_WIRED_HEADSET; 219 if (mDoesDeviceSupportEarpieceRoute) { 220 addedRoutes |= ROUTE_EARPIECE; 221 } 222 break; 223 case DISCONNECT_BLUETOOTH: 224 Log.addEvent(mCallsManager.getForegroundCall(), LogUtils.Events.AUDIO_ROUTE, 225 "Bluetooth disconnected"); 226 removedRoutes |= ROUTE_BLUETOOTH; 227 break; 228 case SWITCH_BASELINE_ROUTE: 229 sendInternalMessage(calculateBaselineRouteMessage(false)); 230 return HANDLED; 231 case USER_SWITCH_BASELINE_ROUTE: 232 sendInternalMessage(calculateBaselineRouteMessage(true)); 233 return HANDLED; 234 case SWITCH_FOCUS: 235 mAudioFocusType = msg.arg1; 236 return NOT_HANDLED; 237 default: 238 return NOT_HANDLED; 239 } 240 241 if (addedRoutes != 0 || removedRoutes != 0) { 242 mAvailableRoutes = modifyRoutes(mAvailableRoutes, removedRoutes, addedRoutes, true); 243 mDeviceSupportedRoutes = modifyRoutes(mDeviceSupportedRoutes, removedRoutes, 244 addedRoutes, false); 245 } 246 247 return NOT_HANDLED; 248 } 249 250 // Behavior will depend on whether the state is an active one or a quiescent one. 251 abstract public void updateSystemAudioState(); 252 abstract public boolean isActive(); 253 } 254 255 class ActiveEarpieceRoute extends EarpieceRoute { 256 @Override 257 public String getName() { 258 return ACTIVE_EARPIECE_ROUTE_NAME; 259 } 260 261 @Override 262 public boolean isActive() { 263 return true; 264 } 265 266 @Override 267 public void enter() { 268 super.enter(); 269 setSpeakerphoneOn(false); 270 setBluetoothOn(false); 271 CallAudioState newState = new CallAudioState(mIsMuted, ROUTE_EARPIECE, 272 mAvailableRoutes); 273 setSystemAudioState(newState, true); 274 updateInternalCallAudioState(); 275 } 276 277 @Override 278 public void updateSystemAudioState() { 279 updateInternalCallAudioState(); 280 setSystemAudioState(mCurrentCallAudioState); 281 } 282 283 @Override 284 public boolean processMessage(Message msg) { 285 if (super.processMessage(msg) == HANDLED) { 286 return HANDLED; 287 } 288 switch (msg.what) { 289 case SWITCH_EARPIECE: 290 case USER_SWITCH_EARPIECE: 291 // Nothing to do here 292 return HANDLED; 293 case SWITCH_BLUETOOTH: 294 case USER_SWITCH_BLUETOOTH: 295 if ((mAvailableRoutes & ROUTE_BLUETOOTH) != 0) { 296 transitionTo(mAudioFocusType == ACTIVE_FOCUS ? 297 mActiveBluetoothRoute : mRingingBluetoothRoute); 298 } else { 299 Log.w(this, "Ignoring switch to bluetooth command. Not available."); 300 } 301 return HANDLED; 302 case SWITCH_HEADSET: 303 case USER_SWITCH_HEADSET: 304 if ((mAvailableRoutes & ROUTE_WIRED_HEADSET) != 0) { 305 transitionTo(mActiveHeadsetRoute); 306 } else { 307 Log.w(this, "Ignoring switch to headset command. Not available."); 308 } 309 return HANDLED; 310 case SWITCH_SPEAKER: 311 case USER_SWITCH_SPEAKER: 312 transitionTo(mActiveSpeakerRoute); 313 return HANDLED; 314 case SWITCH_FOCUS: 315 if (msg.arg1 == NO_FOCUS) { 316 reinitialize(); 317 } 318 return HANDLED; 319 default: 320 return NOT_HANDLED; 321 } 322 } 323 } 324 325 class QuiescentEarpieceRoute extends EarpieceRoute { 326 @Override 327 public String getName() { 328 return QUIESCENT_EARPIECE_ROUTE_NAME; 329 } 330 331 @Override 332 public boolean isActive() { 333 return false; 334 } 335 336 @Override 337 public void enter() { 338 super.enter(); 339 mHasUserExplicitlyLeftBluetooth = false; 340 updateInternalCallAudioState(); 341 } 342 343 @Override 344 public void updateSystemAudioState() { 345 updateInternalCallAudioState(); 346 } 347 348 @Override 349 public boolean processMessage(Message msg) { 350 if (super.processMessage(msg) == HANDLED) { 351 return HANDLED; 352 } 353 switch (msg.what) { 354 case SWITCH_EARPIECE: 355 case USER_SWITCH_EARPIECE: 356 // Nothing to do here 357 return HANDLED; 358 case SWITCH_BLUETOOTH: 359 case USER_SWITCH_BLUETOOTH: 360 if ((mAvailableRoutes & ROUTE_BLUETOOTH) != 0) { 361 transitionTo(mQuiescentBluetoothRoute); 362 } else { 363 Log.w(this, "Ignoring switch to bluetooth command. Not available."); 364 } 365 return HANDLED; 366 case SWITCH_HEADSET: 367 case USER_SWITCH_HEADSET: 368 if ((mAvailableRoutes & ROUTE_WIRED_HEADSET) != 0) { 369 transitionTo(mQuiescentHeadsetRoute); 370 } else { 371 Log.w(this, "Ignoring switch to headset command. Not available."); 372 } 373 return HANDLED; 374 case SWITCH_SPEAKER: 375 case USER_SWITCH_SPEAKER: 376 transitionTo(mQuiescentSpeakerRoute); 377 return HANDLED; 378 case SWITCH_FOCUS: 379 if (msg.arg1 == ACTIVE_FOCUS || msg.arg1 == RINGING_FOCUS) { 380 transitionTo(mActiveEarpieceRoute); 381 } 382 return HANDLED; 383 default: 384 return NOT_HANDLED; 385 } 386 } 387 } 388 389 abstract class EarpieceRoute extends AudioState { 390 @Override 391 public boolean processMessage(Message msg) { 392 if (super.processMessage(msg) == HANDLED) { 393 return HANDLED; 394 } 395 switch (msg.what) { 396 case CONNECT_WIRED_HEADSET: 397 sendInternalMessage(SWITCH_HEADSET); 398 return HANDLED; 399 case CONNECT_BLUETOOTH: 400 if (!mHasUserExplicitlyLeftBluetooth) { 401 sendInternalMessage(SWITCH_BLUETOOTH); 402 } else { 403 Log.i(this, "Not switching to BT route from earpiece because user has " + 404 "explicitly disconnected."); 405 updateSystemAudioState(); 406 } 407 return HANDLED; 408 case DISCONNECT_BLUETOOTH: 409 updateSystemAudioState(); 410 // No change in audio route required 411 return HANDLED; 412 case DISCONNECT_WIRED_HEADSET: 413 Log.e(this, new IllegalStateException(), 414 "Wired headset should not go from connected to not when on " + 415 "earpiece"); 416 updateSystemAudioState(); 417 return HANDLED; 418 case BT_AUDIO_DISCONNECT: 419 // This may be sent as a confirmation by the BT stack after switch off BT. 420 return HANDLED; 421 case CONNECT_DOCK: 422 sendInternalMessage(SWITCH_SPEAKER); 423 return HANDLED; 424 case DISCONNECT_DOCK: 425 // Nothing to do here 426 return HANDLED; 427 default: 428 return NOT_HANDLED; 429 } 430 } 431 } 432 433 class ActiveHeadsetRoute extends HeadsetRoute { 434 @Override 435 public String getName() { 436 return ACTIVE_HEADSET_ROUTE_NAME; 437 } 438 439 @Override 440 public boolean isActive() { 441 return true; 442 } 443 444 @Override 445 public void enter() { 446 super.enter(); 447 setSpeakerphoneOn(false); 448 setBluetoothOn(false); 449 CallAudioState newState = new CallAudioState(mIsMuted, ROUTE_WIRED_HEADSET, 450 mAvailableRoutes); 451 setSystemAudioState(newState, true); 452 updateInternalCallAudioState(); 453 } 454 455 @Override 456 public void updateSystemAudioState() { 457 updateInternalCallAudioState(); 458 setSystemAudioState(mCurrentCallAudioState); 459 } 460 461 @Override 462 public boolean processMessage(Message msg) { 463 if (super.processMessage(msg) == HANDLED) { 464 return HANDLED; 465 } 466 switch (msg.what) { 467 case SWITCH_EARPIECE: 468 case USER_SWITCH_EARPIECE: 469 if ((mAvailableRoutes & ROUTE_EARPIECE) != 0) { 470 transitionTo(mActiveEarpieceRoute); 471 } else { 472 Log.w(this, "Ignoring switch to earpiece command. Not available."); 473 } 474 return HANDLED; 475 case SWITCH_BLUETOOTH: 476 case USER_SWITCH_BLUETOOTH: 477 if ((mAvailableRoutes & ROUTE_BLUETOOTH) != 0) { 478 transitionTo(mAudioFocusType == ACTIVE_FOCUS ? 479 mActiveBluetoothRoute : mRingingBluetoothRoute); 480 } else { 481 Log.w(this, "Ignoring switch to bluetooth command. Not available."); 482 } 483 return HANDLED; 484 case SWITCH_HEADSET: 485 case USER_SWITCH_HEADSET: 486 // Nothing to do 487 return HANDLED; 488 case SWITCH_SPEAKER: 489 case USER_SWITCH_SPEAKER: 490 transitionTo(mActiveSpeakerRoute); 491 return HANDLED; 492 case SWITCH_FOCUS: 493 if (msg.arg1 == NO_FOCUS) { 494 reinitialize(); 495 } 496 return HANDLED; 497 default: 498 return NOT_HANDLED; 499 } 500 } 501 } 502 503 class QuiescentHeadsetRoute extends HeadsetRoute { 504 @Override 505 public String getName() { 506 return QUIESCENT_HEADSET_ROUTE_NAME; 507 } 508 509 @Override 510 public boolean isActive() { 511 return false; 512 } 513 514 @Override 515 public void enter() { 516 super.enter(); 517 mHasUserExplicitlyLeftBluetooth = false; 518 updateInternalCallAudioState(); 519 } 520 521 @Override 522 public void updateSystemAudioState() { 523 updateInternalCallAudioState(); 524 } 525 526 @Override 527 public boolean processMessage(Message msg) { 528 if (super.processMessage(msg) == HANDLED) { 529 return HANDLED; 530 } 531 switch (msg.what) { 532 case SWITCH_EARPIECE: 533 case USER_SWITCH_EARPIECE: 534 if ((mAvailableRoutes & ROUTE_EARPIECE) != 0) { 535 transitionTo(mQuiescentEarpieceRoute); 536 } else { 537 Log.w(this, "Ignoring switch to earpiece command. Not available."); 538 } 539 return HANDLED; 540 case SWITCH_BLUETOOTH: 541 case USER_SWITCH_BLUETOOTH: 542 if ((mAvailableRoutes & ROUTE_BLUETOOTH) != 0) { 543 transitionTo(mQuiescentBluetoothRoute); 544 } else { 545 Log.w(this, "Ignoring switch to bluetooth command. Not available."); 546 } 547 return HANDLED; 548 case SWITCH_HEADSET: 549 case USER_SWITCH_HEADSET: 550 // Nothing to do 551 return HANDLED; 552 case SWITCH_SPEAKER: 553 case USER_SWITCH_SPEAKER: 554 transitionTo(mQuiescentSpeakerRoute); 555 return HANDLED; 556 case SWITCH_FOCUS: 557 if (msg.arg1 == ACTIVE_FOCUS || msg.arg1 == RINGING_FOCUS) { 558 transitionTo(mActiveHeadsetRoute); 559 } 560 return HANDLED; 561 default: 562 return NOT_HANDLED; 563 } 564 } 565 } 566 567 abstract class HeadsetRoute extends AudioState { 568 @Override 569 public boolean processMessage(Message msg) { 570 if (super.processMessage(msg) == HANDLED) { 571 return HANDLED; 572 } 573 switch (msg.what) { 574 case CONNECT_WIRED_HEADSET: 575 Log.e(this, new IllegalStateException(), 576 "Wired headset should already be connected."); 577 mAvailableRoutes |= ROUTE_WIRED_HEADSET; 578 updateSystemAudioState(); 579 return HANDLED; 580 case CONNECT_BLUETOOTH: 581 if (!mHasUserExplicitlyLeftBluetooth) { 582 sendInternalMessage(SWITCH_BLUETOOTH); 583 } else { 584 Log.i(this, "Not switching to BT route from headset because user has " + 585 "explicitly disconnected."); 586 updateSystemAudioState(); 587 } 588 return HANDLED; 589 case DISCONNECT_BLUETOOTH: 590 updateSystemAudioState(); 591 // No change in audio route required 592 return HANDLED; 593 case DISCONNECT_WIRED_HEADSET: 594 if (mWasOnSpeaker) { 595 sendInternalMessage(SWITCH_SPEAKER); 596 } else { 597 sendInternalMessage(SWITCH_BASELINE_ROUTE); 598 } 599 return HANDLED; 600 case BT_AUDIO_DISCONNECT: 601 // This may be sent as a confirmation by the BT stack after switch off BT. 602 return HANDLED; 603 case CONNECT_DOCK: 604 // Nothing to do here 605 return HANDLED; 606 case DISCONNECT_DOCK: 607 // Nothing to do here 608 return HANDLED; 609 default: 610 return NOT_HANDLED; 611 } 612 } 613 } 614 615 class ActiveBluetoothRoute extends BluetoothRoute { 616 @Override 617 public String getName() { 618 return ACTIVE_BLUETOOTH_ROUTE_NAME; 619 } 620 621 @Override 622 public boolean isActive() { 623 return true; 624 } 625 626 @Override 627 public void enter() { 628 super.enter(); 629 setSpeakerphoneOn(false); 630 setBluetoothOn(true); 631 CallAudioState newState = new CallAudioState(mIsMuted, ROUTE_BLUETOOTH, 632 mAvailableRoutes); 633 setSystemAudioState(newState, true); 634 updateInternalCallAudioState(); 635 } 636 637 @Override 638 public void updateSystemAudioState() { 639 updateInternalCallAudioState(); 640 setSystemAudioState(mCurrentCallAudioState); 641 } 642 643 @Override 644 public boolean processMessage(Message msg) { 645 if (super.processMessage(msg) == HANDLED) { 646 return HANDLED; 647 } 648 switch (msg.what) { 649 case USER_SWITCH_EARPIECE: 650 mHasUserExplicitlyLeftBluetooth = true; 651 // fall through 652 case SWITCH_EARPIECE: 653 if ((mAvailableRoutes & ROUTE_EARPIECE) != 0) { 654 transitionTo(mActiveEarpieceRoute); 655 } else { 656 Log.w(this, "Ignoring switch to earpiece command. Not available."); 657 } 658 return HANDLED; 659 case SWITCH_BLUETOOTH: 660 case USER_SWITCH_BLUETOOTH: 661 // Nothing to do 662 return HANDLED; 663 case USER_SWITCH_HEADSET: 664 mHasUserExplicitlyLeftBluetooth = true; 665 // fall through 666 case SWITCH_HEADSET: 667 if ((mAvailableRoutes & ROUTE_WIRED_HEADSET) != 0) { 668 transitionTo(mActiveHeadsetRoute); 669 } else { 670 Log.w(this, "Ignoring switch to headset command. Not available."); 671 } 672 return HANDLED; 673 case USER_SWITCH_SPEAKER: 674 mHasUserExplicitlyLeftBluetooth = true; 675 // fall through 676 case SWITCH_SPEAKER: 677 transitionTo(mActiveSpeakerRoute); 678 return HANDLED; 679 case SWITCH_FOCUS: 680 if (msg.arg1 == NO_FOCUS) { 681 reinitialize(); 682 } else if (msg.arg1 == RINGING_FOCUS) { 683 transitionTo(mRingingBluetoothRoute); 684 } 685 return HANDLED; 686 case BT_AUDIO_DISCONNECT: 687 sendInternalMessage(SWITCH_BASELINE_ROUTE); 688 return HANDLED; 689 default: 690 return NOT_HANDLED; 691 } 692 } 693 } 694 695 class RingingBluetoothRoute extends BluetoothRoute { 696 @Override 697 public String getName() { 698 return RINGING_BLUETOOTH_ROUTE_NAME; 699 } 700 701 @Override 702 public boolean isActive() { 703 return false; 704 } 705 706 @Override 707 public void enter() { 708 super.enter(); 709 setSpeakerphoneOn(false); 710 // Do not enable SCO audio here, since RING is being sent to the headset. 711 CallAudioState newState = new CallAudioState(mIsMuted, ROUTE_BLUETOOTH, 712 mAvailableRoutes); 713 setSystemAudioState(newState); 714 updateInternalCallAudioState(); 715 } 716 717 @Override 718 public void updateSystemAudioState() { 719 updateInternalCallAudioState(); 720 setSystemAudioState(mCurrentCallAudioState); 721 } 722 723 @Override 724 public boolean processMessage(Message msg) { 725 if (super.processMessage(msg) == HANDLED) { 726 return HANDLED; 727 } 728 switch (msg.what) { 729 case USER_SWITCH_EARPIECE: 730 mHasUserExplicitlyLeftBluetooth = true; 731 // fall through 732 case SWITCH_EARPIECE: 733 if ((mAvailableRoutes & ROUTE_EARPIECE) != 0) { 734 transitionTo(mActiveEarpieceRoute); 735 } else { 736 Log.w(this, "Ignoring switch to earpiece command. Not available."); 737 } 738 return HANDLED; 739 case SWITCH_BLUETOOTH: 740 case USER_SWITCH_BLUETOOTH: 741 // Nothing to do 742 return HANDLED; 743 case USER_SWITCH_HEADSET: 744 mHasUserExplicitlyLeftBluetooth = true; 745 // fall through 746 case SWITCH_HEADSET: 747 if ((mAvailableRoutes & ROUTE_WIRED_HEADSET) != 0) { 748 transitionTo(mActiveHeadsetRoute); 749 } else { 750 Log.w(this, "Ignoring switch to headset command. Not available."); 751 } 752 return HANDLED; 753 case USER_SWITCH_SPEAKER: 754 mHasUserExplicitlyLeftBluetooth = true; 755 // fall through 756 case SWITCH_SPEAKER: 757 transitionTo(mActiveSpeakerRoute); 758 return HANDLED; 759 case SWITCH_FOCUS: 760 if (msg.arg1 == NO_FOCUS) { 761 reinitialize(); 762 } else if (msg.arg1 == ACTIVE_FOCUS) { 763 transitionTo(mActiveBluetoothRoute); 764 } 765 return HANDLED; 766 case BT_AUDIO_DISCONNECT: 767 // BT SCO might be connected when in-band ringing is enabled 768 sendInternalMessage(SWITCH_BASELINE_ROUTE); 769 return HANDLED; 770 default: 771 return NOT_HANDLED; 772 } 773 } 774 } 775 776 class QuiescentBluetoothRoute extends BluetoothRoute { 777 @Override 778 public String getName() { 779 return QUIESCENT_BLUETOOTH_ROUTE_NAME; 780 } 781 782 @Override 783 public boolean isActive() { 784 return false; 785 } 786 787 @Override 788 public void enter() { 789 super.enter(); 790 mHasUserExplicitlyLeftBluetooth = false; 791 updateInternalCallAudioState(); 792 setBluetoothOn(false); 793 } 794 795 @Override 796 public void updateSystemAudioState() { 797 updateInternalCallAudioState(); 798 } 799 800 @Override 801 public boolean processMessage(Message msg) { 802 if (super.processMessage(msg) == HANDLED) { 803 return HANDLED; 804 } 805 switch (msg.what) { 806 case SWITCH_EARPIECE: 807 case USER_SWITCH_EARPIECE: 808 if ((mAvailableRoutes & ROUTE_EARPIECE) != 0) { 809 transitionTo(mQuiescentEarpieceRoute); 810 } else { 811 Log.w(this, "Ignoring switch to earpiece command. Not available."); 812 } 813 return HANDLED; 814 case SWITCH_BLUETOOTH: 815 case USER_SWITCH_BLUETOOTH: 816 // Nothing to do 817 return HANDLED; 818 case SWITCH_HEADSET: 819 case USER_SWITCH_HEADSET: 820 if ((mAvailableRoutes & ROUTE_WIRED_HEADSET) != 0) { 821 transitionTo(mQuiescentHeadsetRoute); 822 } else { 823 Log.w(this, "Ignoring switch to headset command. Not available."); 824 } 825 return HANDLED; 826 case SWITCH_SPEAKER: 827 case USER_SWITCH_SPEAKER: 828 transitionTo(mQuiescentSpeakerRoute); 829 return HANDLED; 830 case SWITCH_FOCUS: 831 if (msg.arg1 == ACTIVE_FOCUS) { 832 transitionTo(mActiveBluetoothRoute); 833 } else if (msg.arg1 == RINGING_FOCUS) { 834 transitionTo(mRingingBluetoothRoute); 835 } 836 return HANDLED; 837 case BT_AUDIO_DISCONNECT: 838 // Ignore this -- audio disconnecting while quiescent should not cause a 839 // route switch, since the device is still connected. 840 return HANDLED; 841 default: 842 return NOT_HANDLED; 843 } 844 } 845 } 846 847 abstract class BluetoothRoute extends AudioState { 848 @Override 849 public boolean processMessage(Message msg) { 850 if (super.processMessage(msg) == HANDLED) { 851 return HANDLED; 852 } 853 switch (msg.what) { 854 case CONNECT_WIRED_HEADSET: 855 sendInternalMessage(SWITCH_HEADSET); 856 return HANDLED; 857 case CONNECT_BLUETOOTH: 858 // We can't tell when a change in bluetooth state corresponds to an 859 // actual connection or disconnection, so we'll just ignore it if we're already 860 // in the bluetooth route. 861 return HANDLED; 862 case DISCONNECT_BLUETOOTH: 863 sendInternalMessage(SWITCH_BASELINE_ROUTE); 864 mWasOnSpeaker = false; 865 return HANDLED; 866 case DISCONNECT_WIRED_HEADSET: 867 updateSystemAudioState(); 868 // No change in audio route required 869 return HANDLED; 870 case CONNECT_DOCK: 871 // Nothing to do here 872 return HANDLED; 873 case DISCONNECT_DOCK: 874 // Nothing to do here 875 return HANDLED; 876 default: 877 return NOT_HANDLED; 878 } 879 } 880 } 881 882 class ActiveSpeakerRoute extends SpeakerRoute { 883 @Override 884 public String getName() { 885 return ACTIVE_SPEAKER_ROUTE_NAME; 886 } 887 888 @Override 889 public boolean isActive() { 890 return true; 891 } 892 893 @Override 894 public void enter() { 895 super.enter(); 896 mWasOnSpeaker = true; 897 setSpeakerphoneOn(true); 898 setBluetoothOn(false); 899 CallAudioState newState = new CallAudioState(mIsMuted, ROUTE_SPEAKER, 900 mAvailableRoutes); 901 setSystemAudioState(newState); 902 updateInternalCallAudioState(); 903 } 904 905 @Override 906 public void updateSystemAudioState() { 907 updateInternalCallAudioState(); 908 setSystemAudioState(mCurrentCallAudioState); 909 } 910 911 @Override 912 public boolean processMessage(Message msg) { 913 if (super.processMessage(msg) == HANDLED) { 914 return HANDLED; 915 } 916 switch(msg.what) { 917 case USER_SWITCH_EARPIECE: 918 mWasOnSpeaker = false; 919 // fall through 920 case SWITCH_EARPIECE: 921 if ((mAvailableRoutes & ROUTE_EARPIECE) != 0) { 922 transitionTo(mActiveEarpieceRoute); 923 } else { 924 Log.w(this, "Ignoring switch to earpiece command. Not available."); 925 } 926 return HANDLED; 927 case USER_SWITCH_BLUETOOTH: 928 mWasOnSpeaker = false; 929 // fall through 930 case SWITCH_BLUETOOTH: 931 if ((mAvailableRoutes & ROUTE_BLUETOOTH) != 0) { 932 transitionTo(mAudioFocusType == ACTIVE_FOCUS ? 933 mActiveBluetoothRoute : mRingingBluetoothRoute); 934 } else { 935 Log.w(this, "Ignoring switch to bluetooth command. Not available."); 936 } 937 return HANDLED; 938 case USER_SWITCH_HEADSET: 939 mWasOnSpeaker = false; 940 // fall through 941 case SWITCH_HEADSET: 942 if ((mAvailableRoutes & ROUTE_WIRED_HEADSET) != 0) { 943 transitionTo(mActiveHeadsetRoute); 944 } else { 945 Log.w(this, "Ignoring switch to headset command. Not available."); 946 } 947 return HANDLED; 948 case SWITCH_SPEAKER: 949 case USER_SWITCH_SPEAKER: 950 // Nothing to do 951 return HANDLED; 952 case SWITCH_FOCUS: 953 if (msg.arg1 == NO_FOCUS) { 954 reinitialize(); 955 } 956 return HANDLED; 957 default: 958 return NOT_HANDLED; 959 } 960 } 961 } 962 963 class QuiescentSpeakerRoute extends SpeakerRoute { 964 @Override 965 public String getName() { 966 return QUIESCENT_SPEAKER_ROUTE_NAME; 967 } 968 969 @Override 970 public boolean isActive() { 971 return false; 972 } 973 974 @Override 975 public void enter() { 976 super.enter(); 977 mHasUserExplicitlyLeftBluetooth = false; 978 // Omit setting mWasOnSpeaker to true here, since this does not reflect a call 979 // actually being on speakerphone. 980 updateInternalCallAudioState(); 981 } 982 983 @Override 984 public void updateSystemAudioState() { 985 updateInternalCallAudioState(); 986 } 987 988 @Override 989 public boolean processMessage(Message msg) { 990 if (super.processMessage(msg) == HANDLED) { 991 return HANDLED; 992 } 993 switch(msg.what) { 994 case SWITCH_EARPIECE: 995 case USER_SWITCH_EARPIECE: 996 if ((mAvailableRoutes & ROUTE_EARPIECE) != 0) { 997 transitionTo(mQuiescentEarpieceRoute); 998 } else { 999 Log.w(this, "Ignoring switch to earpiece command. Not available."); 1000 } 1001 return HANDLED; 1002 case SWITCH_BLUETOOTH: 1003 case USER_SWITCH_BLUETOOTH: 1004 if ((mAvailableRoutes & ROUTE_BLUETOOTH) != 0) { 1005 transitionTo(mQuiescentBluetoothRoute); 1006 } else { 1007 Log.w(this, "Ignoring switch to bluetooth command. Not available."); 1008 } 1009 return HANDLED; 1010 case SWITCH_HEADSET: 1011 case USER_SWITCH_HEADSET: 1012 if ((mAvailableRoutes & ROUTE_WIRED_HEADSET) != 0) { 1013 transitionTo(mQuiescentHeadsetRoute); 1014 } else { 1015 Log.w(this, "Ignoring switch to headset command. Not available."); 1016 } 1017 return HANDLED; 1018 case SWITCH_SPEAKER: 1019 case USER_SWITCH_SPEAKER: 1020 // Nothing to do 1021 return HANDLED; 1022 case SWITCH_FOCUS: 1023 if (msg.arg1 == ACTIVE_FOCUS || msg.arg1 == RINGING_FOCUS) { 1024 transitionTo(mActiveSpeakerRoute); 1025 } 1026 return HANDLED; 1027 default: 1028 return NOT_HANDLED; 1029 } 1030 } 1031 } 1032 1033 abstract class SpeakerRoute extends AudioState { 1034 @Override 1035 public boolean processMessage(Message msg) { 1036 if (super.processMessage(msg) == HANDLED) { 1037 return HANDLED; 1038 } 1039 switch (msg.what) { 1040 case CONNECT_WIRED_HEADSET: 1041 sendInternalMessage(SWITCH_HEADSET); 1042 return HANDLED; 1043 case CONNECT_BLUETOOTH: 1044 if (!mHasUserExplicitlyLeftBluetooth) { 1045 sendInternalMessage(SWITCH_BLUETOOTH); 1046 } else { 1047 Log.i(this, "Not switching to BT route from speaker because user has " + 1048 "explicitly disconnected."); 1049 updateSystemAudioState(); 1050 } 1051 return HANDLED; 1052 case DISCONNECT_BLUETOOTH: 1053 updateSystemAudioState(); 1054 // No change in audio route required 1055 return HANDLED; 1056 case DISCONNECT_WIRED_HEADSET: 1057 updateSystemAudioState(); 1058 // No change in audio route required 1059 return HANDLED; 1060 case BT_AUDIO_DISCONNECT: 1061 // This may be sent as a confirmation by the BT stack after switch off BT. 1062 return HANDLED; 1063 case CONNECT_DOCK: 1064 // Nothing to do here 1065 return HANDLED; 1066 case DISCONNECT_DOCK: 1067 sendInternalMessage(SWITCH_BASELINE_ROUTE); 1068 return HANDLED; 1069 default: 1070 return NOT_HANDLED; 1071 } 1072 } 1073 } 1074 1075 private final ActiveEarpieceRoute mActiveEarpieceRoute = new ActiveEarpieceRoute(); 1076 private final ActiveHeadsetRoute mActiveHeadsetRoute = new ActiveHeadsetRoute(); 1077 private final ActiveBluetoothRoute mActiveBluetoothRoute = new ActiveBluetoothRoute(); 1078 private final ActiveSpeakerRoute mActiveSpeakerRoute = new ActiveSpeakerRoute(); 1079 private final RingingBluetoothRoute mRingingBluetoothRoute = new RingingBluetoothRoute(); 1080 private final QuiescentEarpieceRoute mQuiescentEarpieceRoute = new QuiescentEarpieceRoute(); 1081 private final QuiescentHeadsetRoute mQuiescentHeadsetRoute = new QuiescentHeadsetRoute(); 1082 private final QuiescentBluetoothRoute mQuiescentBluetoothRoute = new QuiescentBluetoothRoute(); 1083 private final QuiescentSpeakerRoute mQuiescentSpeakerRoute = new QuiescentSpeakerRoute(); 1084 1085 /** 1086 * A few pieces of hidden state. Used to avoid exponential explosion of number of explicit 1087 * states 1088 */ 1089 private int mDeviceSupportedRoutes; 1090 private int mAvailableRoutes; 1091 private int mAudioFocusType; 1092 private boolean mWasOnSpeaker; 1093 private boolean mIsMuted; 1094 1095 private final Context mContext; 1096 private final CallsManager mCallsManager; 1097 private final AudioManager mAudioManager; 1098 private final BluetoothRouteManager mBluetoothRouteManager; 1099 private final WiredHeadsetManager mWiredHeadsetManager; 1100 private final StatusBarNotifier mStatusBarNotifier; 1101 private final CallAudioManager.AudioServiceFactory mAudioServiceFactory; 1102 private final boolean mDoesDeviceSupportEarpieceRoute; 1103 private final TelecomSystem.SyncRoot mLock; 1104 private boolean mHasUserExplicitlyLeftBluetooth = false; 1105 1106 private HashMap<String, Integer> mStateNameToRouteCode; 1107 private HashMap<Integer, AudioState> mRouteCodeToQuiescentState; 1108 1109 // CallAudioState is used as an interface to communicate with many other system components. 1110 // No internal state transitions should depend on this variable. 1111 private CallAudioState mCurrentCallAudioState; 1112 private CallAudioState mLastKnownCallAudioState; 1113 1114 public CallAudioRouteStateMachine( 1115 Context context, 1116 CallsManager callsManager, 1117 BluetoothRouteManager bluetoothManager, 1118 WiredHeadsetManager wiredHeadsetManager, 1119 StatusBarNotifier statusBarNotifier, 1120 CallAudioManager.AudioServiceFactory audioServiceFactory, 1121 boolean doesDeviceSupportEarpieceRoute) { 1122 super(NAME); 1123 addState(mActiveEarpieceRoute); 1124 addState(mActiveHeadsetRoute); 1125 addState(mActiveBluetoothRoute); 1126 addState(mActiveSpeakerRoute); 1127 addState(mRingingBluetoothRoute); 1128 addState(mQuiescentEarpieceRoute); 1129 addState(mQuiescentHeadsetRoute); 1130 addState(mQuiescentBluetoothRoute); 1131 addState(mQuiescentSpeakerRoute); 1132 1133 mContext = context; 1134 mCallsManager = callsManager; 1135 mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); 1136 mBluetoothRouteManager = bluetoothManager; 1137 mWiredHeadsetManager = wiredHeadsetManager; 1138 mStatusBarNotifier = statusBarNotifier; 1139 mAudioServiceFactory = audioServiceFactory; 1140 mDoesDeviceSupportEarpieceRoute = doesDeviceSupportEarpieceRoute; 1141 mLock = callsManager.getLock(); 1142 1143 mStateNameToRouteCode = new HashMap<>(8); 1144 mStateNameToRouteCode.put(mQuiescentEarpieceRoute.getName(), ROUTE_EARPIECE); 1145 mStateNameToRouteCode.put(mQuiescentBluetoothRoute.getName(), ROUTE_BLUETOOTH); 1146 mStateNameToRouteCode.put(mQuiescentHeadsetRoute.getName(), ROUTE_WIRED_HEADSET); 1147 mStateNameToRouteCode.put(mQuiescentSpeakerRoute.getName(), ROUTE_SPEAKER); 1148 mStateNameToRouteCode.put(mRingingBluetoothRoute.getName(), ROUTE_BLUETOOTH); 1149 mStateNameToRouteCode.put(mActiveEarpieceRoute.getName(), ROUTE_EARPIECE); 1150 mStateNameToRouteCode.put(mActiveBluetoothRoute.getName(), ROUTE_BLUETOOTH); 1151 mStateNameToRouteCode.put(mActiveHeadsetRoute.getName(), ROUTE_WIRED_HEADSET); 1152 mStateNameToRouteCode.put(mActiveSpeakerRoute.getName(), ROUTE_SPEAKER); 1153 1154 mRouteCodeToQuiescentState = new HashMap<>(4); 1155 mRouteCodeToQuiescentState.put(ROUTE_EARPIECE, mQuiescentEarpieceRoute); 1156 mRouteCodeToQuiescentState.put(ROUTE_BLUETOOTH, mQuiescentBluetoothRoute); 1157 mRouteCodeToQuiescentState.put(ROUTE_SPEAKER, mQuiescentSpeakerRoute); 1158 mRouteCodeToQuiescentState.put(ROUTE_WIRED_HEADSET, mQuiescentHeadsetRoute); 1159 } 1160 1161 /** 1162 * Initializes the state machine with info on initial audio route, supported audio routes, 1163 * and mute status. 1164 */ 1165 public void initialize() { 1166 CallAudioState initState = getInitialAudioState(); 1167 initialize(initState); 1168 } 1169 1170 public void initialize(CallAudioState initState) { 1171 if ((initState.getRoute() & getCurrentCallSupportedRoutes()) == 0) { 1172 Log.e(this, new IllegalArgumentException(), "Route %d specified when supported call" + 1173 " routes are: %d", initState.getRoute(), getCurrentCallSupportedRoutes()); 1174 } 1175 1176 mCurrentCallAudioState = initState; 1177 mLastKnownCallAudioState = initState; 1178 mDeviceSupportedRoutes = initState.getSupportedRouteMask(); 1179 mAvailableRoutes = mDeviceSupportedRoutes & getCurrentCallSupportedRoutes(); 1180 mIsMuted = initState.isMuted(); 1181 mWasOnSpeaker = false; 1182 1183 mStatusBarNotifier.notifyMute(initState.isMuted()); 1184 mStatusBarNotifier.notifySpeakerphone(initState.getRoute() == CallAudioState.ROUTE_SPEAKER); 1185 setInitialState(mRouteCodeToQuiescentState.get(initState.getRoute())); 1186 start(); 1187 } 1188 1189 /** 1190 * Getter for the current CallAudioState object that the state machine is keeping track of. 1191 * Used for compatibility purposes. 1192 */ 1193 public CallAudioState getCurrentCallAudioState() { 1194 return mCurrentCallAudioState; 1195 } 1196 1197 public void sendMessageWithSessionInfo(int message, int arg) { 1198 sendMessage(message, arg, 0, Log.createSubsession()); 1199 } 1200 1201 public void sendMessageWithSessionInfo(int message) { 1202 sendMessage(message, 0, 0, Log.createSubsession()); 1203 } 1204 1205 /** 1206 * This is for state-independent changes in audio route (i.e. muting or runnables) 1207 * @param msg that couldn't be handled. 1208 */ 1209 @Override 1210 protected void unhandledMessage(Message msg) { 1211 CallAudioState newCallAudioState; 1212 switch (msg.what) { 1213 case MUTE_ON: 1214 setMuteOn(true); 1215 newCallAudioState = new CallAudioState(mIsMuted, 1216 mCurrentCallAudioState.getRoute(), 1217 mAvailableRoutes); 1218 setSystemAudioState(newCallAudioState); 1219 updateInternalCallAudioState(); 1220 return; 1221 case MUTE_OFF: 1222 setMuteOn(false); 1223 newCallAudioState = new CallAudioState(mIsMuted, 1224 mCurrentCallAudioState.getRoute(), 1225 mAvailableRoutes); 1226 setSystemAudioState(newCallAudioState); 1227 updateInternalCallAudioState(); 1228 return; 1229 case TOGGLE_MUTE: 1230 if (mIsMuted) { 1231 sendInternalMessage(MUTE_OFF); 1232 } else { 1233 sendInternalMessage(MUTE_ON); 1234 } 1235 return; 1236 case UPDATE_SYSTEM_AUDIO_ROUTE: 1237 updateRouteForForegroundCall(); 1238 resendSystemAudioState(); 1239 return; 1240 case RUN_RUNNABLE: 1241 java.lang.Runnable r = (java.lang.Runnable) msg.obj; 1242 r.run(); 1243 return; 1244 default: 1245 Log.e(this, new IllegalStateException(), 1246 "Unexpected message code %d", msg.what); 1247 } 1248 } 1249 1250 public void quitStateMachine() { 1251 quitNow(); 1252 } 1253 1254 public void dumpPendingMessages(IndentingPrintWriter pw) { 1255 getHandler().getLooper().dump(pw::println, ""); 1256 } 1257 1258 public boolean isHfpDeviceAvailable() { 1259 return mBluetoothRouteManager.isBluetoothAvailable(); 1260 } 1261 1262 private void setSpeakerphoneOn(boolean on) { 1263 if (mAudioManager.isSpeakerphoneOn() != on) { 1264 Log.i(this, "turning speaker phone %s", on); 1265 mAudioManager.setSpeakerphoneOn(on); 1266 mStatusBarNotifier.notifySpeakerphone(on); 1267 } 1268 } 1269 1270 private void setBluetoothOn(boolean on) { 1271 if (mBluetoothRouteManager.isBluetoothAvailable()) { 1272 if (on != mBluetoothRouteManager.isBluetoothAudioConnectedOrPending()) { 1273 Log.i(this, "connecting bluetooth %s", on); 1274 if (on) { 1275 mBluetoothRouteManager.connectBluetoothAudio(null /*TODO: add real address*/); 1276 } else { 1277 mBluetoothRouteManager.disconnectBluetoothAudio(); 1278 } 1279 } 1280 } 1281 } 1282 1283 private void setMuteOn(boolean mute) { 1284 mIsMuted = mute; 1285 Log.addEvent(mCallsManager.getForegroundCall(), mute ? 1286 LogUtils.Events.MUTE : LogUtils.Events.UNMUTE); 1287 if (mute != mAudioManager.isMicrophoneMute() && isInActiveState()) { 1288 IAudioService audio = mAudioServiceFactory.getAudioService(); 1289 Log.i(this, "changing microphone mute state to: %b [serviceIsNull=%b]", 1290 mute, audio == null); 1291 if (audio != null) { 1292 try { 1293 // We use the audio service directly here so that we can specify 1294 // the current user. Telecom runs in the system_server process which 1295 // may run as a separate user from the foreground user. If we 1296 // used AudioManager directly, we would change mute for the system's 1297 // user and not the current foreground, which we want to avoid. 1298 audio.setMicrophoneMute( 1299 mute, mContext.getOpPackageName(), getCurrentUserId()); 1300 mStatusBarNotifier.notifyMute(mute); 1301 1302 } catch (RemoteException e) { 1303 Log.e(this, e, "Remote exception while toggling mute."); 1304 } 1305 // TODO: Check microphone state after attempting to set to ensure that 1306 // our state corroborates AudioManager's state. 1307 } 1308 } 1309 } 1310 1311 /** 1312 * Updates the CallAudioState object from current internal state. The result is used for 1313 * external communication only. 1314 */ 1315 private void updateInternalCallAudioState() { 1316 IState currentState = getCurrentState(); 1317 if (currentState == null) { 1318 Log.e(this, new IllegalStateException(), "Current state should never be null" + 1319 " when updateInternalCallAudioState is called."); 1320 mCurrentCallAudioState = new CallAudioState( 1321 mIsMuted, mCurrentCallAudioState.getRoute(), mAvailableRoutes); 1322 return; 1323 } 1324 int currentRoute = mStateNameToRouteCode.get(currentState.getName()); 1325 mCurrentCallAudioState = new CallAudioState(mIsMuted, currentRoute, mAvailableRoutes); 1326 } 1327 1328 private void setSystemAudioState(CallAudioState newCallAudioState) { 1329 setSystemAudioState(newCallAudioState, false); 1330 } 1331 1332 private void resendSystemAudioState() { 1333 setSystemAudioState(mLastKnownCallAudioState, true); 1334 } 1335 1336 private void setSystemAudioState(CallAudioState newCallAudioState, boolean force) { 1337 synchronized (mLock) { 1338 Log.i(this, "setSystemAudioState: changing from %s to %s", mLastKnownCallAudioState, 1339 newCallAudioState); 1340 if (force || !newCallAudioState.equals(mLastKnownCallAudioState)) { 1341 if (newCallAudioState.getRoute() != mLastKnownCallAudioState.getRoute()) { 1342 Log.addEvent(mCallsManager.getForegroundCall(), 1343 AUDIO_ROUTE_TO_LOG_EVENT.get(newCallAudioState.getRoute(), 1344 LogUtils.Events.AUDIO_ROUTE)); 1345 } 1346 1347 mCallsManager.onCallAudioStateChanged(mLastKnownCallAudioState, newCallAudioState); 1348 updateAudioForForegroundCall(newCallAudioState); 1349 mLastKnownCallAudioState = newCallAudioState; 1350 } 1351 } 1352 } 1353 1354 private void updateAudioForForegroundCall(CallAudioState newCallAudioState) { 1355 Call call = mCallsManager.getForegroundCall(); 1356 if (call != null && call.getConnectionService() != null) { 1357 call.getConnectionService().onCallAudioStateChanged(call, newCallAudioState); 1358 } 1359 } 1360 1361 private int calculateSupportedRoutes() { 1362 int routeMask = CallAudioState.ROUTE_SPEAKER; 1363 1364 if (mWiredHeadsetManager.isPluggedIn()) { 1365 routeMask |= CallAudioState.ROUTE_WIRED_HEADSET; 1366 } else if (mDoesDeviceSupportEarpieceRoute){ 1367 routeMask |= CallAudioState.ROUTE_EARPIECE; 1368 } 1369 1370 if (mBluetoothRouteManager.isBluetoothAvailable()) { 1371 routeMask |= CallAudioState.ROUTE_BLUETOOTH; 1372 } 1373 1374 return routeMask; 1375 } 1376 1377 private void sendInternalMessage(int messageCode) { 1378 // Internal messages are messages which the state machine sends to itself in the 1379 // course of processing externally-sourced messages. We want to send these messages at 1380 // the front of the queue in order to make actions appear atomic to the user and to 1381 // prevent scenarios such as these: 1382 // 1. State machine handler thread is suspended for some reason. 1383 // 2. Headset gets connected (sends CONNECT_HEADSET). 1384 // 3. User switches to speakerphone in the UI (sends SWITCH_SPEAKER). 1385 // 4. State machine handler is un-suspended. 1386 // 5. State machine handler processes the CONNECT_HEADSET message and sends 1387 // SWITCH_HEADSET at end of queue. 1388 // 6. State machine handler processes SWITCH_SPEAKER. 1389 // 7. State machine handler processes SWITCH_HEADSET. 1390 Session subsession = Log.createSubsession(); 1391 if(subsession != null) { 1392 sendMessageAtFrontOfQueue(messageCode, subsession); 1393 } else { 1394 sendMessageAtFrontOfQueue(messageCode); 1395 } 1396 } 1397 1398 private CallAudioState getInitialAudioState() { 1399 int supportedRouteMask = calculateSupportedRoutes() & getCurrentCallSupportedRoutes(); 1400 final int route; 1401 1402 if ((supportedRouteMask & ROUTE_BLUETOOTH) != 0) { 1403 route = ROUTE_BLUETOOTH; 1404 } else if ((supportedRouteMask & ROUTE_WIRED_HEADSET) != 0) { 1405 route = ROUTE_WIRED_HEADSET; 1406 } else if ((supportedRouteMask & ROUTE_EARPIECE) != 0) { 1407 route = ROUTE_EARPIECE; 1408 } else { 1409 route = ROUTE_SPEAKER; 1410 } 1411 1412 return new CallAudioState(false, route, supportedRouteMask); 1413 } 1414 1415 private int getCurrentUserId() { 1416 final long ident = Binder.clearCallingIdentity(); 1417 try { 1418 UserInfo currentUser = ActivityManager.getService().getCurrentUser(); 1419 return currentUser.id; 1420 } catch (RemoteException e) { 1421 // Activity manager not running, nothing we can do assume user 0. 1422 } finally { 1423 Binder.restoreCallingIdentity(ident); 1424 } 1425 return UserHandle.USER_OWNER; 1426 } 1427 1428 private boolean isInActiveState() { 1429 AudioState currentState = (AudioState) getCurrentState(); 1430 if (currentState == null) { 1431 Log.w(this, "Current state is null, assuming inactive state"); 1432 return false; 1433 } 1434 return currentState.isActive(); 1435 } 1436 1437 public static boolean doesDeviceSupportEarpieceRoute() { 1438 String[] characteristics = SystemProperties.get("ro.build.characteristics").split(","); 1439 for (String characteristic : characteristics) { 1440 if ("watch".equals(characteristic)) { 1441 return false; 1442 } 1443 } 1444 return true; 1445 } 1446 1447 private int calculateBaselineRouteMessage(boolean isExplicitUserRequest) { 1448 boolean isSkipEarpiece = false; 1449 if (!isExplicitUserRequest) { 1450 synchronized (mLock) { 1451 // Check video calls to skip earpiece since the baseline for video 1452 // calls should be the speakerphone route 1453 isSkipEarpiece = mCallsManager.hasVideoCall(); 1454 } 1455 } 1456 if ((mAvailableRoutes & ROUTE_EARPIECE) != 0 && !isSkipEarpiece) { 1457 return isExplicitUserRequest ? USER_SWITCH_EARPIECE : SWITCH_EARPIECE; 1458 } else if ((mAvailableRoutes & ROUTE_WIRED_HEADSET) != 0) { 1459 return isExplicitUserRequest ? USER_SWITCH_HEADSET : SWITCH_HEADSET; 1460 } else { 1461 return isExplicitUserRequest ? USER_SWITCH_SPEAKER : SWITCH_SPEAKER; 1462 } 1463 } 1464 1465 private void reinitialize() { 1466 CallAudioState initState = getInitialAudioState(); 1467 mDeviceSupportedRoutes = initState.getSupportedRouteMask(); 1468 mAvailableRoutes = mDeviceSupportedRoutes & getCurrentCallSupportedRoutes(); 1469 mIsMuted = initState.isMuted(); 1470 setMuteOn(mIsMuted); 1471 mWasOnSpeaker = false; 1472 mHasUserExplicitlyLeftBluetooth = false; 1473 mLastKnownCallAudioState = initState; 1474 transitionTo(mRouteCodeToQuiescentState.get(initState.getRoute())); 1475 } 1476 1477 private void updateRouteForForegroundCall() { 1478 mAvailableRoutes = mDeviceSupportedRoutes & getCurrentCallSupportedRoutes(); 1479 1480 CallAudioState currentState = getCurrentCallAudioState(); 1481 1482 // Move to baseline route in the case the current route is no longer available. 1483 if ((mAvailableRoutes & currentState.getRoute()) == 0) { 1484 sendInternalMessage(calculateBaselineRouteMessage(false)); 1485 } 1486 } 1487 1488 private int getCurrentCallSupportedRoutes() { 1489 int supportedRoutes = CallAudioState.ROUTE_ALL; 1490 1491 if (mCallsManager.getForegroundCall() != null) { 1492 supportedRoutes &= mCallsManager.getForegroundCall().getSupportedAudioRoutes(); 1493 } 1494 1495 return supportedRoutes; 1496 } 1497 1498 private int modifyRoutes(int base, int remove, int add, boolean considerCurrentCall) { 1499 base &= ~remove; 1500 1501 if (considerCurrentCall) { 1502 add &= getCurrentCallSupportedRoutes(); 1503 } 1504 1505 base |= add; 1506 1507 return base; 1508 } 1509} 1510