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> 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(MUTE_ON, "MUTE_ON"); 132 put(MUTE_OFF, "MUTE_OFF"); 133 put(TOGGLE_MUTE, "TOGGLE_MUTE"); 134 135 put(SWITCH_FOCUS, "SWITCH_FOCUS"); 136 137 put(RUN_RUNNABLE, "RUN_RUNNABLE"); 138 }}; 139 140 private static final String ACTIVE_EARPIECE_ROUTE_NAME = "ActiveEarpieceRoute"; 141 private static final String ACTIVE_BLUETOOTH_ROUTE_NAME = "ActiveBluetoothRoute"; 142 private static final String ACTIVE_SPEAKER_ROUTE_NAME = "ActiveSpeakerRoute"; 143 private static final String ACTIVE_HEADSET_ROUTE_NAME = "ActiveHeadsetRoute"; 144 private static final String QUIESCENT_EARPIECE_ROUTE_NAME = "QuiescentEarpieceRoute"; 145 private static final String QUIESCENT_BLUETOOTH_ROUTE_NAME = "QuiescentBluetoothRoute"; 146 private static final String QUIESCENT_SPEAKER_ROUTE_NAME = "QuiescentSpeakerRoute"; 147 private static final String QUIESCENT_HEADSET_ROUTE_NAME = "QuiescentHeadsetRoute"; 148 149 public static final String NAME = CallAudioRouteStateMachine.class.getName(); 150 151 @Override onPreHandleMessage(Message msg)152 protected void onPreHandleMessage(Message msg) { 153 if (msg.obj != null && msg.obj instanceof Session) { 154 String messageCodeName = MESSAGE_CODE_TO_NAME.get(msg.what, "unknown"); 155 Log.continueSession((Session) msg.obj, "CARSM.pM_" + messageCodeName); 156 Log.i(this, "Message received: %s=%d", messageCodeName, msg.what); 157 } 158 } 159 160 @Override onPostHandleMessage(Message msg)161 protected void onPostHandleMessage(Message msg) { 162 Log.endSession(); 163 } 164 165 abstract class AudioState extends State { 166 @Override enter()167 public void enter() { 168 super.enter(); 169 Log.event(mCallsManager.getForegroundCall(), Log.Events.AUDIO_ROUTE, 170 "Entering state " + getName()); 171 } 172 173 @Override exit()174 public void exit() { 175 Log.event(mCallsManager.getForegroundCall(), Log.Events.AUDIO_ROUTE, 176 "Leaving state " + getName()); 177 super.exit(); 178 } 179 180 @Override processMessage(Message msg)181 public boolean processMessage(Message msg) { 182 switch (msg.what) { 183 case CONNECT_WIRED_HEADSET: 184 Log.event(mCallsManager.getForegroundCall(), Log.Events.AUDIO_ROUTE, 185 "Wired headset connected"); 186 mAvailableRoutes &= ~ROUTE_EARPIECE; 187 mAvailableRoutes |= ROUTE_WIRED_HEADSET; 188 return NOT_HANDLED; 189 case CONNECT_BLUETOOTH: 190 Log.event(mCallsManager.getForegroundCall(), Log.Events.AUDIO_ROUTE, 191 "Bluetooth connected"); 192 mAvailableRoutes |= ROUTE_BLUETOOTH; 193 return NOT_HANDLED; 194 case DISCONNECT_WIRED_HEADSET: 195 Log.event(mCallsManager.getForegroundCall(), Log.Events.AUDIO_ROUTE, 196 "Wired headset disconnected"); 197 mAvailableRoutes &= ~ROUTE_WIRED_HEADSET; 198 if (mDoesDeviceSupportEarpieceRoute) { 199 mAvailableRoutes |= ROUTE_EARPIECE; 200 } 201 return NOT_HANDLED; 202 case DISCONNECT_BLUETOOTH: 203 Log.event(mCallsManager.getForegroundCall(), Log.Events.AUDIO_ROUTE, 204 "Bluetooth disconnected"); 205 mAvailableRoutes &= ~ROUTE_BLUETOOTH; 206 return NOT_HANDLED; 207 case SWITCH_BASELINE_ROUTE: 208 sendInternalMessage(calculateBaselineRouteMessage(false)); 209 return HANDLED; 210 case USER_SWITCH_BASELINE_ROUTE: 211 sendInternalMessage(calculateBaselineRouteMessage(true)); 212 return HANDLED; 213 default: 214 return NOT_HANDLED; 215 } 216 } 217 218 // Behavior will depend on whether the state is an active one or a quiescent one. updateSystemAudioState()219 abstract public void updateSystemAudioState(); isActive()220 abstract public boolean isActive(); 221 } 222 223 class ActiveEarpieceRoute extends EarpieceRoute { 224 @Override getName()225 public String getName() { 226 return ACTIVE_EARPIECE_ROUTE_NAME; 227 } 228 229 @Override isActive()230 public boolean isActive() { 231 return true; 232 } 233 234 @Override enter()235 public void enter() { 236 super.enter(); 237 setSpeakerphoneOn(false); 238 setBluetoothOn(false); 239 CallAudioState newState = new CallAudioState(mIsMuted, ROUTE_EARPIECE, 240 mAvailableRoutes); 241 setSystemAudioState(newState); 242 updateInternalCallAudioState(); 243 } 244 245 @Override updateSystemAudioState()246 public void updateSystemAudioState() { 247 updateInternalCallAudioState(); 248 setSystemAudioState(mCurrentCallAudioState); 249 } 250 251 @Override processMessage(Message msg)252 public boolean processMessage(Message msg) { 253 if (super.processMessage(msg) == HANDLED) { 254 return HANDLED; 255 } 256 switch (msg.what) { 257 case SWITCH_EARPIECE: 258 case USER_SWITCH_EARPIECE: 259 // Nothing to do here 260 return HANDLED; 261 case SWITCH_BLUETOOTH: 262 case USER_SWITCH_BLUETOOTH: 263 if ((mAvailableRoutes & ROUTE_BLUETOOTH) != 0) { 264 transitionTo(mActiveBluetoothRoute); 265 } else { 266 Log.w(this, "Ignoring switch to bluetooth command. Not available."); 267 } 268 return HANDLED; 269 case SWITCH_HEADSET: 270 case USER_SWITCH_HEADSET: 271 if ((mAvailableRoutes & ROUTE_WIRED_HEADSET) != 0) { 272 transitionTo(mActiveHeadsetRoute); 273 } else { 274 Log.w(this, "Ignoring switch to headset command. Not available."); 275 } 276 return HANDLED; 277 case SWITCH_SPEAKER: 278 case USER_SWITCH_SPEAKER: 279 transitionTo(mActiveSpeakerRoute); 280 return HANDLED; 281 case SWITCH_FOCUS: 282 if (msg.arg1 == NO_FOCUS) { 283 reinitialize(); 284 } 285 return HANDLED; 286 default: 287 return NOT_HANDLED; 288 } 289 } 290 } 291 292 class QuiescentEarpieceRoute extends EarpieceRoute { 293 @Override getName()294 public String getName() { 295 return QUIESCENT_EARPIECE_ROUTE_NAME; 296 } 297 298 @Override isActive()299 public boolean isActive() { 300 return false; 301 } 302 303 @Override enter()304 public void enter() { 305 super.enter(); 306 mHasUserExplicitlyLeftBluetooth = false; 307 updateInternalCallAudioState(); 308 } 309 310 @Override updateSystemAudioState()311 public void updateSystemAudioState() { 312 updateInternalCallAudioState(); 313 } 314 315 @Override processMessage(Message msg)316 public boolean processMessage(Message msg) { 317 if (super.processMessage(msg) == HANDLED) { 318 return HANDLED; 319 } 320 switch (msg.what) { 321 case SWITCH_EARPIECE: 322 // Nothing to do here 323 return HANDLED; 324 case SWITCH_BLUETOOTH: 325 if ((mAvailableRoutes & ROUTE_BLUETOOTH) != 0) { 326 transitionTo(mQuiescentBluetoothRoute); 327 } else { 328 Log.w(this, "Ignoring switch to bluetooth command. Not available."); 329 } 330 return HANDLED; 331 case SWITCH_HEADSET: 332 if ((mAvailableRoutes & ROUTE_WIRED_HEADSET) != 0) { 333 transitionTo(mQuiescentHeadsetRoute); 334 } else { 335 Log.w(this, "Ignoring switch to headset command. Not available."); 336 } 337 return HANDLED; 338 case SWITCH_SPEAKER: 339 transitionTo(mQuiescentSpeakerRoute); 340 return HANDLED; 341 case SWITCH_FOCUS: 342 if (msg.arg1 == HAS_FOCUS) { 343 transitionTo(mActiveEarpieceRoute); 344 } 345 return HANDLED; 346 default: 347 return NOT_HANDLED; 348 } 349 } 350 } 351 352 abstract class EarpieceRoute extends AudioState { 353 @Override processMessage(Message msg)354 public boolean processMessage(Message msg) { 355 if (super.processMessage(msg) == HANDLED) { 356 return HANDLED; 357 } 358 switch (msg.what) { 359 case CONNECT_WIRED_HEADSET: 360 sendInternalMessage(SWITCH_HEADSET); 361 return HANDLED; 362 case CONNECT_BLUETOOTH: 363 if (!mHasUserExplicitlyLeftBluetooth) { 364 sendInternalMessage(SWITCH_BLUETOOTH); 365 } else { 366 Log.i(this, "Not switching to BT route from earpiece because user has " + 367 "explicitly disconnected."); 368 updateSystemAudioState(); 369 } 370 return HANDLED; 371 case DISCONNECT_BLUETOOTH: 372 updateSystemAudioState(); 373 // No change in audio route required 374 return HANDLED; 375 case DISCONNECT_WIRED_HEADSET: 376 Log.e(this, new IllegalStateException(), 377 "Wired headset should not go from connected to not when on " + 378 "earpiece"); 379 updateSystemAudioState(); 380 return HANDLED; 381 case BT_AUDIO_DISCONNECT: 382 // This may be sent as a confirmation by the BT stack after switch off BT. 383 return HANDLED; 384 case CONNECT_DOCK: 385 sendInternalMessage(SWITCH_SPEAKER); 386 return HANDLED; 387 case DISCONNECT_DOCK: 388 // Nothing to do here 389 return HANDLED; 390 default: 391 return NOT_HANDLED; 392 } 393 } 394 } 395 396 class ActiveHeadsetRoute extends HeadsetRoute { 397 @Override getName()398 public String getName() { 399 return ACTIVE_HEADSET_ROUTE_NAME; 400 } 401 402 @Override isActive()403 public boolean isActive() { 404 return true; 405 } 406 407 @Override enter()408 public void enter() { 409 super.enter(); 410 setSpeakerphoneOn(false); 411 setBluetoothOn(false); 412 CallAudioState newState = new CallAudioState(mIsMuted, ROUTE_WIRED_HEADSET, 413 mAvailableRoutes); 414 setSystemAudioState(newState); 415 updateInternalCallAudioState(); 416 } 417 418 @Override updateSystemAudioState()419 public void updateSystemAudioState() { 420 updateInternalCallAudioState(); 421 setSystemAudioState(mCurrentCallAudioState); 422 } 423 424 @Override processMessage(Message msg)425 public boolean processMessage(Message msg) { 426 if (super.processMessage(msg) == HANDLED) { 427 return HANDLED; 428 } 429 switch (msg.what) { 430 case SWITCH_EARPIECE: 431 case USER_SWITCH_EARPIECE: 432 if ((mAvailableRoutes & ROUTE_EARPIECE) != 0) { 433 transitionTo(mActiveEarpieceRoute); 434 } else { 435 Log.w(this, "Ignoring switch to earpiece command. Not available."); 436 } 437 return HANDLED; 438 case SWITCH_BLUETOOTH: 439 case USER_SWITCH_BLUETOOTH: 440 if ((mAvailableRoutes & ROUTE_BLUETOOTH) != 0) { 441 transitionTo(mActiveBluetoothRoute); 442 } else { 443 Log.w(this, "Ignoring switch to bluetooth command. Not available."); 444 } 445 return HANDLED; 446 case SWITCH_HEADSET: 447 case USER_SWITCH_HEADSET: 448 // Nothing to do 449 return HANDLED; 450 case SWITCH_SPEAKER: 451 case USER_SWITCH_SPEAKER: 452 transitionTo(mActiveSpeakerRoute); 453 return HANDLED; 454 case SWITCH_FOCUS: 455 if (msg.arg1 == NO_FOCUS) { 456 reinitialize(); 457 } 458 return HANDLED; 459 default: 460 return NOT_HANDLED; 461 } 462 } 463 } 464 465 class QuiescentHeadsetRoute extends HeadsetRoute { 466 @Override getName()467 public String getName() { 468 return QUIESCENT_HEADSET_ROUTE_NAME; 469 } 470 471 @Override isActive()472 public boolean isActive() { 473 return false; 474 } 475 476 @Override enter()477 public void enter() { 478 super.enter(); 479 mHasUserExplicitlyLeftBluetooth = false; 480 updateInternalCallAudioState(); 481 } 482 483 @Override updateSystemAudioState()484 public void updateSystemAudioState() { 485 updateInternalCallAudioState(); 486 } 487 488 @Override processMessage(Message msg)489 public boolean processMessage(Message msg) { 490 if (super.processMessage(msg) == HANDLED) { 491 return HANDLED; 492 } 493 switch (msg.what) { 494 case SWITCH_EARPIECE: 495 if ((mAvailableRoutes & ROUTE_EARPIECE) != 0) { 496 transitionTo(mQuiescentEarpieceRoute); 497 } else { 498 Log.w(this, "Ignoring switch to earpiece command. Not available."); 499 } 500 return HANDLED; 501 case SWITCH_BLUETOOTH: 502 if ((mAvailableRoutes & ROUTE_BLUETOOTH) != 0) { 503 transitionTo(mQuiescentBluetoothRoute); 504 } else { 505 Log.w(this, "Ignoring switch to bluetooth command. Not available."); 506 } 507 return HANDLED; 508 case SWITCH_HEADSET: 509 // Nothing to do 510 return HANDLED; 511 case SWITCH_SPEAKER: 512 transitionTo(mQuiescentSpeakerRoute); 513 return HANDLED; 514 case SWITCH_FOCUS: 515 if (msg.arg1 == HAS_FOCUS) { 516 transitionTo(mActiveHeadsetRoute); 517 } 518 return HANDLED; 519 default: 520 return NOT_HANDLED; 521 } 522 } 523 } 524 525 abstract class HeadsetRoute extends AudioState { 526 @Override processMessage(Message msg)527 public boolean processMessage(Message msg) { 528 if (super.processMessage(msg) == HANDLED) { 529 return HANDLED; 530 } 531 switch (msg.what) { 532 case CONNECT_WIRED_HEADSET: 533 Log.e(this, new IllegalStateException(), 534 "Wired headset should already be connected."); 535 mAvailableRoutes |= ROUTE_WIRED_HEADSET; 536 updateSystemAudioState(); 537 return HANDLED; 538 case CONNECT_BLUETOOTH: 539 if (!mHasUserExplicitlyLeftBluetooth) { 540 sendInternalMessage(SWITCH_BLUETOOTH); 541 } else { 542 Log.i(this, "Not switching to BT route from headset because user has " + 543 "explicitly disconnected."); 544 updateSystemAudioState(); 545 } 546 return HANDLED; 547 case DISCONNECT_BLUETOOTH: 548 updateSystemAudioState(); 549 // No change in audio route required 550 return HANDLED; 551 case DISCONNECT_WIRED_HEADSET: 552 if (mWasOnSpeaker) { 553 sendInternalMessage(SWITCH_SPEAKER); 554 } else { 555 sendInternalMessage(SWITCH_BASELINE_ROUTE); 556 } 557 return HANDLED; 558 case BT_AUDIO_DISCONNECT: 559 // This may be sent as a confirmation by the BT stack after switch off BT. 560 return HANDLED; 561 case CONNECT_DOCK: 562 // Nothing to do here 563 return HANDLED; 564 case DISCONNECT_DOCK: 565 // Nothing to do here 566 return HANDLED; 567 default: 568 return NOT_HANDLED; 569 } 570 } 571 } 572 573 class ActiveBluetoothRoute extends BluetoothRoute { 574 @Override getName()575 public String getName() { 576 return ACTIVE_BLUETOOTH_ROUTE_NAME; 577 } 578 579 @Override isActive()580 public boolean isActive() { 581 return true; 582 } 583 584 @Override enter()585 public void enter() { 586 super.enter(); 587 setSpeakerphoneOn(false); 588 setBluetoothOn(true); 589 CallAudioState newState = new CallAudioState(mIsMuted, ROUTE_BLUETOOTH, 590 mAvailableRoutes); 591 setSystemAudioState(newState); 592 updateInternalCallAudioState(); 593 } 594 595 @Override updateSystemAudioState()596 public void updateSystemAudioState() { 597 updateInternalCallAudioState(); 598 setSystemAudioState(mCurrentCallAudioState); 599 } 600 601 @Override processMessage(Message msg)602 public boolean processMessage(Message msg) { 603 if (super.processMessage(msg) == HANDLED) { 604 return HANDLED; 605 } 606 switch (msg.what) { 607 case USER_SWITCH_EARPIECE: 608 mHasUserExplicitlyLeftBluetooth = true; 609 // fall through 610 case SWITCH_EARPIECE: 611 if ((mAvailableRoutes & ROUTE_EARPIECE) != 0) { 612 transitionTo(mActiveEarpieceRoute); 613 } else { 614 Log.w(this, "Ignoring switch to earpiece command. Not available."); 615 } 616 return HANDLED; 617 case SWITCH_BLUETOOTH: 618 case USER_SWITCH_BLUETOOTH: 619 // Nothing to do 620 return HANDLED; 621 case USER_SWITCH_HEADSET: 622 mHasUserExplicitlyLeftBluetooth = true; 623 // fall through 624 case SWITCH_HEADSET: 625 if ((mAvailableRoutes & ROUTE_WIRED_HEADSET) != 0) { 626 transitionTo(mActiveHeadsetRoute); 627 } else { 628 Log.w(this, "Ignoring switch to headset command. Not available."); 629 } 630 return HANDLED; 631 case USER_SWITCH_SPEAKER: 632 mHasUserExplicitlyLeftBluetooth = true; 633 // fall through 634 case SWITCH_SPEAKER: 635 transitionTo(mActiveSpeakerRoute); 636 return HANDLED; 637 case SWITCH_FOCUS: 638 if (msg.arg1 == NO_FOCUS) { 639 reinitialize(); 640 } 641 return HANDLED; 642 case BT_AUDIO_DISCONNECT: 643 sendInternalMessage(SWITCH_BASELINE_ROUTE); 644 return HANDLED; 645 default: 646 return NOT_HANDLED; 647 } 648 } 649 } 650 651 class QuiescentBluetoothRoute extends BluetoothRoute { 652 @Override getName()653 public String getName() { 654 return QUIESCENT_BLUETOOTH_ROUTE_NAME; 655 } 656 657 @Override isActive()658 public boolean isActive() { 659 return false; 660 } 661 662 @Override enter()663 public void enter() { 664 super.enter(); 665 mHasUserExplicitlyLeftBluetooth = false; 666 updateInternalCallAudioState(); 667 } 668 669 @Override updateSystemAudioState()670 public void updateSystemAudioState() { 671 updateInternalCallAudioState(); 672 } 673 674 @Override processMessage(Message msg)675 public boolean processMessage(Message msg) { 676 if (super.processMessage(msg) == HANDLED) { 677 return HANDLED; 678 } 679 switch (msg.what) { 680 case SWITCH_EARPIECE: 681 if ((mAvailableRoutes & ROUTE_EARPIECE) != 0) { 682 transitionTo(mQuiescentEarpieceRoute); 683 } else { 684 Log.w(this, "Ignoring switch to earpiece command. Not available."); 685 } 686 return HANDLED; 687 case SWITCH_BLUETOOTH: 688 // Nothing to do 689 return HANDLED; 690 case SWITCH_HEADSET: 691 if ((mAvailableRoutes & ROUTE_WIRED_HEADSET) != 0) { 692 transitionTo(mQuiescentHeadsetRoute); 693 } else { 694 Log.w(this, "Ignoring switch to headset command. Not available."); 695 } 696 return HANDLED; 697 case SWITCH_SPEAKER: 698 transitionTo(mQuiescentSpeakerRoute); 699 return HANDLED; 700 case SWITCH_FOCUS: 701 if (msg.arg1 == HAS_FOCUS) { 702 transitionTo(mActiveBluetoothRoute); 703 } 704 return HANDLED; 705 case BT_AUDIO_DISCONNECT: 706 // Ignore this -- audio disconnecting while quiescent should not cause a 707 // route switch, since the device is still connected. 708 return HANDLED; 709 default: 710 return NOT_HANDLED; 711 } 712 } 713 } 714 715 abstract class BluetoothRoute extends AudioState { 716 @Override processMessage(Message msg)717 public boolean processMessage(Message msg) { 718 if (super.processMessage(msg) == HANDLED) { 719 return HANDLED; 720 } 721 switch (msg.what) { 722 case CONNECT_WIRED_HEADSET: 723 sendInternalMessage(SWITCH_HEADSET); 724 return HANDLED; 725 case CONNECT_BLUETOOTH: 726 // We can't tell when a change in bluetooth state corresponds to an 727 // actual connection or disconnection, so we'll just ignore it if we're already 728 // in the bluetooth route. 729 return HANDLED; 730 case DISCONNECT_BLUETOOTH: 731 sendInternalMessage(SWITCH_BASELINE_ROUTE); 732 mWasOnSpeaker = false; 733 return HANDLED; 734 case DISCONNECT_WIRED_HEADSET: 735 updateSystemAudioState(); 736 // No change in audio route required 737 return HANDLED; 738 case CONNECT_DOCK: 739 // Nothing to do here 740 return HANDLED; 741 case DISCONNECT_DOCK: 742 // Nothing to do here 743 return HANDLED; 744 default: 745 return NOT_HANDLED; 746 } 747 } 748 } 749 750 class ActiveSpeakerRoute extends SpeakerRoute { 751 @Override getName()752 public String getName() { 753 return ACTIVE_SPEAKER_ROUTE_NAME; 754 } 755 756 @Override isActive()757 public boolean isActive() { 758 return true; 759 } 760 761 @Override enter()762 public void enter() { 763 super.enter(); 764 mWasOnSpeaker = true; 765 setSpeakerphoneOn(true); 766 setBluetoothOn(false); 767 CallAudioState newState = new CallAudioState(mIsMuted, ROUTE_SPEAKER, 768 mAvailableRoutes); 769 setSystemAudioState(newState); 770 updateInternalCallAudioState(); 771 } 772 773 @Override updateSystemAudioState()774 public void updateSystemAudioState() { 775 updateInternalCallAudioState(); 776 setSystemAudioState(mCurrentCallAudioState); 777 } 778 779 @Override processMessage(Message msg)780 public boolean processMessage(Message msg) { 781 if (super.processMessage(msg) == HANDLED) { 782 return HANDLED; 783 } 784 switch(msg.what) { 785 case USER_SWITCH_EARPIECE: 786 mWasOnSpeaker = false; 787 // fall through 788 case SWITCH_EARPIECE: 789 if ((mAvailableRoutes & ROUTE_EARPIECE) != 0) { 790 transitionTo(mActiveEarpieceRoute); 791 } else { 792 Log.w(this, "Ignoring switch to earpiece command. Not available."); 793 } 794 return HANDLED; 795 case USER_SWITCH_BLUETOOTH: 796 mWasOnSpeaker = false; 797 // fall through 798 case SWITCH_BLUETOOTH: 799 if ((mAvailableRoutes & ROUTE_BLUETOOTH) != 0) { 800 transitionTo(mActiveBluetoothRoute); 801 } else { 802 Log.w(this, "Ignoring switch to bluetooth command. Not available."); 803 } 804 return HANDLED; 805 case USER_SWITCH_HEADSET: 806 mWasOnSpeaker = false; 807 // fall through 808 case 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 reinitialize(); 822 } 823 return HANDLED; 824 default: 825 return NOT_HANDLED; 826 } 827 } 828 } 829 830 class QuiescentSpeakerRoute extends SpeakerRoute { 831 @Override getName()832 public String getName() { 833 return QUIESCENT_SPEAKER_ROUTE_NAME; 834 } 835 836 @Override isActive()837 public boolean isActive() { 838 return false; 839 } 840 841 @Override enter()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 updateSystemAudioState()851 public void updateSystemAudioState() { 852 updateInternalCallAudioState(); 853 } 854 855 @Override processMessage(Message msg)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 processMessage(Message msg)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 CallAudioRouteStateMachine( Context context, CallsManager callsManager, BluetoothManager bluetoothManager, WiredHeadsetManager wiredHeadsetManager, StatusBarNotifier statusBarNotifier, CallAudioManager.AudioServiceFactory audioServiceFactory, boolean doesDeviceSupportEarpieceRoute)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 */ initialize()1021 public void initialize() { 1022 CallAudioState initState = getInitialAudioState(); 1023 initialize(initState); 1024 } 1025 initialize(CallAudioState initState)1026 public void initialize(CallAudioState initState) { 1027 mCurrentCallAudioState = initState; 1028 mLastKnownCallAudioState = initState; 1029 mAvailableRoutes = initState.getSupportedRouteMask(); 1030 mIsMuted = initState.isMuted(); 1031 mWasOnSpeaker = false; 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 */ getCurrentCallAudioState()1043 public CallAudioState getCurrentCallAudioState() { 1044 return mCurrentCallAudioState; 1045 } 1046 sendMessageWithSessionInfo(int message, int arg)1047 public void sendMessageWithSessionInfo(int message, int arg) { 1048 sendMessage(message, arg, 0, Log.createSubsession()); 1049 } 1050 sendMessageWithSessionInfo(int message)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 unhandledMessage(Message msg)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 UPDATE_SYSTEM_AUDIO_ROUTE: 1087 resendSystemAudioState(); 1088 return; 1089 case RUN_RUNNABLE: 1090 java.lang.Runnable r = (java.lang.Runnable) msg.obj; 1091 r.run(); 1092 return; 1093 default: 1094 Log.e(this, new IllegalStateException(), 1095 "Unexpected message code"); 1096 } 1097 } 1098 quitStateMachine()1099 public void quitStateMachine() { 1100 quitNow(); 1101 } 1102 setSpeakerphoneOn(boolean on)1103 private void setSpeakerphoneOn(boolean on) { 1104 if (mAudioManager.isSpeakerphoneOn() != on) { 1105 Log.i(this, "turning speaker phone %s", on); 1106 mAudioManager.setSpeakerphoneOn(on); 1107 } 1108 } 1109 setBluetoothOn(boolean on)1110 private void setBluetoothOn(boolean on) { 1111 if (mBluetoothManager.isBluetoothAvailable()) { 1112 boolean isAlreadyOn = mBluetoothManager.isBluetoothAudioConnectedOrPending(); 1113 if (on != isAlreadyOn) { 1114 Log.i(this, "connecting bluetooth %s", on); 1115 if (on) { 1116 mBluetoothManager.connectBluetoothAudio(); 1117 } else { 1118 mBluetoothManager.disconnectBluetoothAudio(); 1119 } 1120 } 1121 } 1122 } 1123 setMuteOn(boolean mute)1124 private void setMuteOn(boolean mute) { 1125 mIsMuted = mute; 1126 Log.event(mCallsManager.getForegroundCall(), Log.Events.MUTE, 1127 mute ? "on" : "off"); 1128 if (mute != mAudioManager.isMicrophoneMute() && isInActiveState()) { 1129 IAudioService audio = mAudioServiceFactory.getAudioService(); 1130 Log.i(this, "changing microphone mute state to: %b [serviceIsNull=%b]", 1131 mute, audio == null); 1132 if (audio != null) { 1133 try { 1134 // We use the audio service directly here so that we can specify 1135 // the current user. Telecom runs in the system_server process which 1136 // may run as a separate user from the foreground user. If we 1137 // used AudioManager directly, we would change mute for the system's 1138 // user and not the current foreground, which we want to avoid. 1139 audio.setMicrophoneMute( 1140 mute, mContext.getOpPackageName(), getCurrentUserId()); 1141 1142 } catch (RemoteException e) { 1143 Log.e(this, e, "Remote exception while toggling mute."); 1144 } 1145 // TODO: Check microphone state after attempting to set to ensure that 1146 // our state corroborates AudioManager's state. 1147 } 1148 } 1149 } 1150 1151 /** 1152 * Updates the CallAudioState object from current internal state. The result is used for 1153 * external communication only. 1154 */ updateInternalCallAudioState()1155 private void updateInternalCallAudioState() { 1156 IState currentState = getCurrentState(); 1157 if (currentState == null) { 1158 Log.e(this, new IllegalStateException(), "Current state should never be null" + 1159 " when updateInternalCallAudioState is called."); 1160 mCurrentCallAudioState = new CallAudioState( 1161 mIsMuted, mCurrentCallAudioState.getRoute(), mAvailableRoutes); 1162 return; 1163 } 1164 int currentRoute = mStateNameToRouteCode.get(currentState.getName()); 1165 mCurrentCallAudioState = new CallAudioState(mIsMuted, currentRoute, mAvailableRoutes); 1166 } 1167 setSystemAudioState(CallAudioState newCallAudioState)1168 private void setSystemAudioState(CallAudioState newCallAudioState) { 1169 setSystemAudioState(newCallAudioState, false); 1170 } 1171 resendSystemAudioState()1172 private void resendSystemAudioState() { 1173 setSystemAudioState(mLastKnownCallAudioState, true); 1174 } 1175 setSystemAudioState(CallAudioState newCallAudioState, boolean force)1176 private void setSystemAudioState(CallAudioState newCallAudioState, boolean force) { 1177 Log.i(this, "setSystemAudioState: changing from %s to %s", mLastKnownCallAudioState, 1178 newCallAudioState); 1179 Log.event(mCallsManager.getForegroundCall(), Log.Events.AUDIO_ROUTE, 1180 CallAudioState.audioRouteToString(newCallAudioState.getRoute())); 1181 1182 if (force || !newCallAudioState.equals(mLastKnownCallAudioState)) { 1183 mCallsManager.onCallAudioStateChanged(mLastKnownCallAudioState, newCallAudioState); 1184 updateAudioForForegroundCall(newCallAudioState); 1185 mLastKnownCallAudioState = newCallAudioState; 1186 } 1187 } 1188 updateAudioForForegroundCall(CallAudioState newCallAudioState)1189 private void updateAudioForForegroundCall(CallAudioState newCallAudioState) { 1190 Call call = mCallsManager.getForegroundCall(); 1191 if (call != null && call.getConnectionService() != null) { 1192 call.getConnectionService().onCallAudioStateChanged(call, newCallAudioState); 1193 } 1194 } 1195 calculateSupportedRoutes()1196 private int calculateSupportedRoutes() { 1197 int routeMask = CallAudioState.ROUTE_SPEAKER; 1198 1199 if (mWiredHeadsetManager.isPluggedIn()) { 1200 routeMask |= CallAudioState.ROUTE_WIRED_HEADSET; 1201 } else if (mDoesDeviceSupportEarpieceRoute){ 1202 routeMask |= CallAudioState.ROUTE_EARPIECE; 1203 } 1204 1205 if (mBluetoothManager.isBluetoothAvailable()) { 1206 routeMask |= CallAudioState.ROUTE_BLUETOOTH; 1207 } 1208 1209 return routeMask; 1210 } 1211 sendInternalMessage(int messageCode)1212 private void sendInternalMessage(int messageCode) { 1213 // Internal messages are messages which the state machine sends to itself in the 1214 // course of processing externally-sourced messages. We want to send these messages at 1215 // the front of the queue in order to make actions appear atomic to the user and to 1216 // prevent scenarios such as these: 1217 // 1. State machine handler thread is suspended for some reason. 1218 // 2. Headset gets connected (sends CONNECT_HEADSET). 1219 // 3. User switches to speakerphone in the UI (sends SWITCH_SPEAKER). 1220 // 4. State machine handler is un-suspended. 1221 // 5. State machine handler processes the CONNECT_HEADSET message and sends 1222 // SWITCH_HEADSET at end of queue. 1223 // 6. State machine handler processes SWITCH_SPEAKER. 1224 // 7. State machine handler processes SWITCH_HEADSET. 1225 Session subsession = Log.createSubsession(); 1226 if(subsession != null) { 1227 sendMessageAtFrontOfQueue(messageCode, subsession); 1228 } else { 1229 sendMessageAtFrontOfQueue(messageCode); 1230 } 1231 } 1232 getInitialAudioState()1233 private CallAudioState getInitialAudioState() { 1234 int supportedRouteMask = calculateSupportedRoutes(); 1235 final int route; 1236 1237 if ((supportedRouteMask & ROUTE_BLUETOOTH) != 0) { 1238 route = ROUTE_BLUETOOTH; 1239 } else if ((supportedRouteMask & ROUTE_WIRED_HEADSET) != 0) { 1240 route = ROUTE_WIRED_HEADSET; 1241 } else if ((supportedRouteMask & ROUTE_EARPIECE) != 0) { 1242 route = ROUTE_EARPIECE; 1243 } else { 1244 route = ROUTE_SPEAKER; 1245 } 1246 1247 return new CallAudioState(false, route, supportedRouteMask); 1248 } 1249 getCurrentUserId()1250 private int getCurrentUserId() { 1251 final long ident = Binder.clearCallingIdentity(); 1252 try { 1253 UserInfo currentUser = ActivityManagerNative.getDefault().getCurrentUser(); 1254 return currentUser.id; 1255 } catch (RemoteException e) { 1256 // Activity manager not running, nothing we can do assume user 0. 1257 } finally { 1258 Binder.restoreCallingIdentity(ident); 1259 } 1260 return UserHandle.USER_OWNER; 1261 } 1262 isInActiveState()1263 private boolean isInActiveState() { 1264 AudioState currentState = (AudioState) getCurrentState(); 1265 if (currentState == null) { 1266 Log.w(this, "Current state is null, assuming inactive state"); 1267 return false; 1268 } 1269 return currentState.isActive(); 1270 } 1271 doesDeviceSupportEarpieceRoute()1272 public static boolean doesDeviceSupportEarpieceRoute() { 1273 String[] characteristics = SystemProperties.get("ro.build.characteristics").split(","); 1274 for (String characteristic : characteristics) { 1275 if ("watch".equals(characteristic)) { 1276 return false; 1277 } 1278 } 1279 return true; 1280 } 1281 calculateBaselineRouteMessage(boolean isExplicitUserRequest)1282 private int calculateBaselineRouteMessage(boolean isExplicitUserRequest) { 1283 if ((mAvailableRoutes & ROUTE_EARPIECE) != 0) { 1284 return isExplicitUserRequest ? USER_SWITCH_EARPIECE : SWITCH_EARPIECE; 1285 } else if ((mAvailableRoutes & ROUTE_WIRED_HEADSET) != 0) { 1286 return isExplicitUserRequest ? USER_SWITCH_HEADSET : SWITCH_HEADSET; 1287 } else if (!mDoesDeviceSupportEarpieceRoute) { 1288 return isExplicitUserRequest ? USER_SWITCH_SPEAKER : SWITCH_SPEAKER; 1289 } else { 1290 Log.e(this, new IllegalStateException(), 1291 "Neither headset nor earpiece are available, but there is an " + 1292 "earpiece on the device. Defaulting to earpiece."); 1293 return isExplicitUserRequest ? USER_SWITCH_EARPIECE : SWITCH_EARPIECE; 1294 } 1295 } 1296 reinitialize()1297 private void reinitialize() { 1298 CallAudioState initState = getInitialAudioState(); 1299 mAvailableRoutes = initState.getSupportedRouteMask(); 1300 mIsMuted = initState.isMuted(); 1301 setMuteOn(mIsMuted); 1302 mWasOnSpeaker = false; 1303 mHasUserExplicitlyLeftBluetooth = false; 1304 transitionTo(mRouteCodeToQuiescentState.get(initState.getRoute())); 1305 } 1306 }