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