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