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