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