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.content.Context; 22 import android.content.pm.UserInfo; 23 import android.media.AudioManager; 24 import android.media.IAudioService; 25 import android.os.Binder; 26 import android.os.Message; 27 import android.os.RemoteException; 28 import android.os.SystemProperties; 29 import android.os.UserHandle; 30 import android.telecom.CallAudioState; 31 import android.telecom.Log; 32 import android.telecom.Logging.Session; 33 import android.util.SparseArray; 34 35 import com.android.internal.util.IState; 36 import com.android.internal.util.IndentingPrintWriter; 37 import com.android.internal.util.State; 38 import com.android.internal.util.StateMachine; 39 import com.android.server.telecom.bluetooth.BluetoothRouteManager; 40 41 import 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 */ 64 public 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 onPreHandleMessage(Message msg)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 onPostHandleMessage(Message msg)179 protected void onPostHandleMessage(Message msg) { 180 Log.endSession(); 181 } 182 183 abstract class AudioState extends State { 184 @Override enter()185 public void enter() { 186 super.enter(); 187 Log.addEvent(mCallsManager.getForegroundCall(), LogUtils.Events.AUDIO_ROUTE, 188 "Entering state " + getName()); 189 } 190 191 @Override exit()192 public void exit() { 193 Log.addEvent(mCallsManager.getForegroundCall(), LogUtils.Events.AUDIO_ROUTE, 194 "Leaving state " + getName()); 195 super.exit(); 196 } 197 198 @Override processMessage(Message msg)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. updateSystemAudioState()251 abstract public void updateSystemAudioState(); isActive()252 abstract public boolean isActive(); 253 } 254 255 class ActiveEarpieceRoute extends EarpieceRoute { 256 @Override getName()257 public String getName() { 258 return ACTIVE_EARPIECE_ROUTE_NAME; 259 } 260 261 @Override isActive()262 public boolean isActive() { 263 return true; 264 } 265 266 @Override enter()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 updateSystemAudioState()278 public void updateSystemAudioState() { 279 updateInternalCallAudioState(); 280 setSystemAudioState(mCurrentCallAudioState); 281 } 282 283 @Override processMessage(Message msg)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 getName()327 public String getName() { 328 return QUIESCENT_EARPIECE_ROUTE_NAME; 329 } 330 331 @Override isActive()332 public boolean isActive() { 333 return false; 334 } 335 336 @Override enter()337 public void enter() { 338 super.enter(); 339 mHasUserExplicitlyLeftBluetooth = false; 340 updateInternalCallAudioState(); 341 } 342 343 @Override updateSystemAudioState()344 public void updateSystemAudioState() { 345 updateInternalCallAudioState(); 346 } 347 348 @Override processMessage(Message msg)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 processMessage(Message msg)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 getName()435 public String getName() { 436 return ACTIVE_HEADSET_ROUTE_NAME; 437 } 438 439 @Override isActive()440 public boolean isActive() { 441 return true; 442 } 443 444 @Override enter()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 updateSystemAudioState()456 public void updateSystemAudioState() { 457 updateInternalCallAudioState(); 458 setSystemAudioState(mCurrentCallAudioState); 459 } 460 461 @Override processMessage(Message msg)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 getName()505 public String getName() { 506 return QUIESCENT_HEADSET_ROUTE_NAME; 507 } 508 509 @Override isActive()510 public boolean isActive() { 511 return false; 512 } 513 514 @Override enter()515 public void enter() { 516 super.enter(); 517 mHasUserExplicitlyLeftBluetooth = false; 518 updateInternalCallAudioState(); 519 } 520 521 @Override updateSystemAudioState()522 public void updateSystemAudioState() { 523 updateInternalCallAudioState(); 524 } 525 526 @Override processMessage(Message msg)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 processMessage(Message msg)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 getName()617 public String getName() { 618 return ACTIVE_BLUETOOTH_ROUTE_NAME; 619 } 620 621 @Override isActive()622 public boolean isActive() { 623 return true; 624 } 625 626 @Override enter()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 updateSystemAudioState()638 public void updateSystemAudioState() { 639 updateInternalCallAudioState(); 640 setSystemAudioState(mCurrentCallAudioState); 641 } 642 643 @Override processMessage(Message msg)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 getName()697 public String getName() { 698 return RINGING_BLUETOOTH_ROUTE_NAME; 699 } 700 701 @Override isActive()702 public boolean isActive() { 703 return false; 704 } 705 706 @Override enter()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 updateSystemAudioState()718 public void updateSystemAudioState() { 719 updateInternalCallAudioState(); 720 setSystemAudioState(mCurrentCallAudioState); 721 } 722 723 @Override processMessage(Message msg)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 getName()778 public String getName() { 779 return QUIESCENT_BLUETOOTH_ROUTE_NAME; 780 } 781 782 @Override isActive()783 public boolean isActive() { 784 return false; 785 } 786 787 @Override enter()788 public void enter() { 789 super.enter(); 790 mHasUserExplicitlyLeftBluetooth = false; 791 updateInternalCallAudioState(); 792 setBluetoothOn(false); 793 } 794 795 @Override updateSystemAudioState()796 public void updateSystemAudioState() { 797 updateInternalCallAudioState(); 798 } 799 800 @Override processMessage(Message msg)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 processMessage(Message msg)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 getName()884 public String getName() { 885 return ACTIVE_SPEAKER_ROUTE_NAME; 886 } 887 888 @Override isActive()889 public boolean isActive() { 890 return true; 891 } 892 893 @Override enter()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 updateSystemAudioState()906 public void updateSystemAudioState() { 907 updateInternalCallAudioState(); 908 setSystemAudioState(mCurrentCallAudioState); 909 } 910 911 @Override processMessage(Message msg)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 getName()965 public String getName() { 966 return QUIESCENT_SPEAKER_ROUTE_NAME; 967 } 968 969 @Override isActive()970 public boolean isActive() { 971 return false; 972 } 973 974 @Override enter()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 updateSystemAudioState()984 public void updateSystemAudioState() { 985 updateInternalCallAudioState(); 986 } 987 988 @Override processMessage(Message msg)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 processMessage(Message msg)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 CallAudioRouteStateMachine( Context context, CallsManager callsManager, BluetoothRouteManager bluetoothManager, WiredHeadsetManager wiredHeadsetManager, StatusBarNotifier statusBarNotifier, CallAudioManager.AudioServiceFactory audioServiceFactory, boolean doesDeviceSupportEarpieceRoute)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 */ initialize()1165 public void initialize() { 1166 CallAudioState initState = getInitialAudioState(); 1167 initialize(initState); 1168 } 1169 initialize(CallAudioState initState)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 */ getCurrentCallAudioState()1193 public CallAudioState getCurrentCallAudioState() { 1194 return mCurrentCallAudioState; 1195 } 1196 sendMessageWithSessionInfo(int message, int arg)1197 public void sendMessageWithSessionInfo(int message, int arg) { 1198 sendMessage(message, arg, 0, Log.createSubsession()); 1199 } 1200 sendMessageWithSessionInfo(int message)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 unhandledMessage(Message msg)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 quitStateMachine()1250 public void quitStateMachine() { 1251 quitNow(); 1252 } 1253 dumpPendingMessages(IndentingPrintWriter pw)1254 public void dumpPendingMessages(IndentingPrintWriter pw) { 1255 getHandler().getLooper().dump(pw::println, ""); 1256 } 1257 isHfpDeviceAvailable()1258 public boolean isHfpDeviceAvailable() { 1259 return mBluetoothRouteManager.isBluetoothAvailable(); 1260 } 1261 setSpeakerphoneOn(boolean on)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 setBluetoothOn(boolean on)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 setMuteOn(boolean mute)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 */ updateInternalCallAudioState()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 setSystemAudioState(CallAudioState newCallAudioState)1328 private void setSystemAudioState(CallAudioState newCallAudioState) { 1329 setSystemAudioState(newCallAudioState, false); 1330 } 1331 resendSystemAudioState()1332 private void resendSystemAudioState() { 1333 setSystemAudioState(mLastKnownCallAudioState, true); 1334 } 1335 setSystemAudioState(CallAudioState newCallAudioState, boolean force)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 updateAudioForForegroundCall(CallAudioState newCallAudioState)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 calculateSupportedRoutes()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 sendInternalMessage(int messageCode)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 getInitialAudioState()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 getCurrentUserId()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 isInActiveState()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 doesDeviceSupportEarpieceRoute()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 calculateBaselineRouteMessage(boolean isExplicitUserRequest)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 reinitialize()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 updateRouteForForegroundCall()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 getCurrentCallSupportedRoutes()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 modifyRoutes(int base, int remove, int add, boolean considerCurrentCall)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