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 onPreHandleMessage(Message msg)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 onPostHandleMessage(Message msg)168 protected void onPostHandleMessage(Message msg) { 169 Log.endSession(); 170 } 171 172 abstract class AudioState extends State { 173 @Override enter()174 public void enter() { 175 super.enter(); 176 Log.event(mCallsManager.getForegroundCall(), Log.Events.AUDIO_ROUTE, 177 "Entering state " + getName()); 178 } 179 180 @Override exit()181 public void exit() { 182 Log.event(mCallsManager.getForegroundCall(), Log.Events.AUDIO_ROUTE, 183 "Leaving state " + getName()); 184 super.exit(); 185 } 186 187 @Override processMessage(Message msg)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. updateSystemAudioState()226 abstract public void updateSystemAudioState(); isActive()227 abstract public boolean isActive(); 228 } 229 230 class ActiveEarpieceRoute extends EarpieceRoute { 231 @Override getName()232 public String getName() { 233 return ACTIVE_EARPIECE_ROUTE_NAME; 234 } 235 236 @Override isActive()237 public boolean isActive() { 238 return true; 239 } 240 241 @Override enter()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 updateSystemAudioState()253 public void updateSystemAudioState() { 254 updateInternalCallAudioState(); 255 setSystemAudioState(mCurrentCallAudioState); 256 } 257 258 @Override processMessage(Message msg)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 getName()301 public String getName() { 302 return QUIESCENT_EARPIECE_ROUTE_NAME; 303 } 304 305 @Override isActive()306 public boolean isActive() { 307 return false; 308 } 309 310 @Override enter()311 public void enter() { 312 super.enter(); 313 mHasUserExplicitlyLeftBluetooth = false; 314 updateInternalCallAudioState(); 315 } 316 317 @Override updateSystemAudioState()318 public void updateSystemAudioState() { 319 updateInternalCallAudioState(); 320 } 321 322 @Override processMessage(Message msg)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 processMessage(Message msg)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 getName()409 public String getName() { 410 return ACTIVE_HEADSET_ROUTE_NAME; 411 } 412 413 @Override isActive()414 public boolean isActive() { 415 return true; 416 } 417 418 @Override enter()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 updateSystemAudioState()430 public void updateSystemAudioState() { 431 updateInternalCallAudioState(); 432 setSystemAudioState(mCurrentCallAudioState); 433 } 434 435 @Override processMessage(Message msg)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 getName()478 public String getName() { 479 return QUIESCENT_HEADSET_ROUTE_NAME; 480 } 481 482 @Override isActive()483 public boolean isActive() { 484 return false; 485 } 486 487 @Override enter()488 public void enter() { 489 super.enter(); 490 mHasUserExplicitlyLeftBluetooth = false; 491 updateInternalCallAudioState(); 492 } 493 494 @Override updateSystemAudioState()495 public void updateSystemAudioState() { 496 updateInternalCallAudioState(); 497 } 498 499 @Override processMessage(Message msg)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 processMessage(Message msg)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 getName()590 public String getName() { 591 return ACTIVE_BLUETOOTH_ROUTE_NAME; 592 } 593 594 @Override isActive()595 public boolean isActive() { 596 return true; 597 } 598 599 @Override enter()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 updateSystemAudioState()611 public void updateSystemAudioState() { 612 updateInternalCallAudioState(); 613 setSystemAudioState(mCurrentCallAudioState); 614 } 615 616 @Override processMessage(Message msg)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 getName()668 public String getName() { 669 return QUIESCENT_BLUETOOTH_ROUTE_NAME; 670 } 671 672 @Override isActive()673 public boolean isActive() { 674 return false; 675 } 676 677 @Override enter()678 public void enter() { 679 super.enter(); 680 mHasUserExplicitlyLeftBluetooth = false; 681 updateInternalCallAudioState(); 682 } 683 684 @Override updateSystemAudioState()685 public void updateSystemAudioState() { 686 updateInternalCallAudioState(); 687 } 688 689 @Override processMessage(Message msg)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 processMessage(Message msg)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 getName()771 public String getName() { 772 return ACTIVE_SPEAKER_ROUTE_NAME; 773 } 774 775 @Override isActive()776 public boolean isActive() { 777 return true; 778 } 779 780 @Override enter()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 updateSystemAudioState()793 public void updateSystemAudioState() { 794 updateInternalCallAudioState(); 795 setSystemAudioState(mCurrentCallAudioState); 796 } 797 798 @Override processMessage(Message msg)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 getName()851 public String getName() { 852 return QUIESCENT_SPEAKER_ROUTE_NAME; 853 } 854 855 @Override isActive()856 public boolean isActive() { 857 return false; 858 } 859 860 @Override enter()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 updateSystemAudioState()870 public void updateSystemAudioState() { 871 updateInternalCallAudioState(); 872 } 873 874 @Override processMessage(Message msg)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 processMessage(Message msg)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 CallAudioRouteStateMachine( Context context, CallsManager callsManager, BluetoothManager bluetoothManager, WiredHeadsetManager wiredHeadsetManager, StatusBarNotifier statusBarNotifier, CallAudioManager.AudioServiceFactory audioServiceFactory, boolean doesDeviceSupportEarpieceRoute)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 */ initialize()1044 public void initialize() { 1045 CallAudioState initState = getInitialAudioState(); 1046 initialize(initState); 1047 } 1048 initialize(CallAudioState initState)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 */ getCurrentCallAudioState()1066 public CallAudioState getCurrentCallAudioState() { 1067 return mCurrentCallAudioState; 1068 } 1069 sendMessageWithSessionInfo(int message, int arg)1070 public void sendMessageWithSessionInfo(int message, int arg) { 1071 sendMessage(message, arg, 0, Log.createSubsession()); 1072 } 1073 sendMessageWithSessionInfo(int message)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 unhandledMessage(Message msg)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 quitStateMachine()1122 public void quitStateMachine() { 1123 quitNow(); 1124 } 1125 setSpeakerphoneOn(boolean on)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 setBluetoothOn(boolean on)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 setMuteOn(boolean mute)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 */ updateInternalCallAudioState()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 setSystemAudioState(CallAudioState newCallAudioState)1193 private void setSystemAudioState(CallAudioState newCallAudioState) { 1194 setSystemAudioState(newCallAudioState, false); 1195 } 1196 resendSystemAudioState()1197 private void resendSystemAudioState() { 1198 setSystemAudioState(mLastKnownCallAudioState, true); 1199 } 1200 setSystemAudioState(CallAudioState newCallAudioState, boolean force)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 updateAudioForForegroundCall(CallAudioState newCallAudioState)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 calculateSupportedRoutes()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 sendInternalMessage(int messageCode)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 getInitialAudioState()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 getCurrentUserId()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 isInActiveState()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 doesDeviceSupportEarpieceRoute()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 calculateBaselineRouteMessage(boolean isExplicitUserRequest)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 reinitialize()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