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