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.Handler; 32 import android.os.Looper; 33 import android.os.Message; 34 import android.os.RemoteException; 35 import android.os.UserHandle; 36 import android.telecom.CallAudioState; 37 import android.telecom.Log; 38 import android.telecom.Logging.Session; 39 import android.util.SparseArray; 40 41 import com.android.internal.annotations.VisibleForTesting; 42 import com.android.internal.os.SomeArgs; 43 import com.android.internal.util.IState; 44 import com.android.internal.util.IndentingPrintWriter; 45 import com.android.internal.util.State; 46 import com.android.internal.util.StateMachine; 47 import com.android.server.telecom.bluetooth.BluetoothRouteManager; 48 import com.android.server.telecom.flags.FeatureFlags; 49 50 import java.util.Collection; 51 import java.util.HashMap; 52 import java.util.Objects; 53 import java.util.Set; 54 import java.util.concurrent.Executor; 55 56 /** 57 * This class describes the available routes of a call as a state machine. 58 * Transitions are caused solely by the commands sent as messages. Possible values for msg.what 59 * are defined as event constants in this file. 60 * 61 * The eight states are all instances of the abstract base class, {@link AudioState}. Each state 62 * is a combination of one of the four audio routes (earpiece, wired headset, bluetooth, and 63 * speakerphone) and audio focus status (active or quiescent). 64 * 65 * Messages are processed first by the processMessage method in the base class, AudioState. 66 * Any messages not completely handled by AudioState are further processed by the same method in 67 * the route-specific abstract classes: {@link EarpieceRoute}, {@link HeadsetRoute}, 68 * {@link BluetoothRoute}, and {@link SpeakerRoute}. Finally, messages that are not handled at 69 * this level are then processed by the classes corresponding to the state instances themselves. 70 * 71 * There are several variables carrying additional state. These include: 72 * mAvailableRoutes: A bitmask describing which audio routes are available 73 * mWasOnSpeaker: A boolean indicating whether we should switch to speakerphone after disconnecting 74 * from a wired headset 75 * mIsMuted: a boolean indicating whether the audio is muted 76 */ 77 public class CallAudioRouteStateMachine extends StateMachine implements CallAudioRouteAdapter { 78 79 public static class Factory { create( Context context, CallsManager callsManager, BluetoothRouteManager bluetoothManager, WiredHeadsetManager wiredHeadsetManager, StatusBarNotifier statusBarNotifier, CallAudioManager.AudioServiceFactory audioServiceFactory, int earpieceControl, Executor asyncTaskExecutor, CallAudioCommunicationDeviceTracker communicationDeviceTracker, FeatureFlags featureFlags)80 public CallAudioRouteStateMachine create( 81 Context context, 82 CallsManager callsManager, 83 BluetoothRouteManager bluetoothManager, 84 WiredHeadsetManager wiredHeadsetManager, 85 StatusBarNotifier statusBarNotifier, 86 CallAudioManager.AudioServiceFactory audioServiceFactory, 87 int earpieceControl, 88 Executor asyncTaskExecutor, 89 CallAudioCommunicationDeviceTracker communicationDeviceTracker, 90 FeatureFlags featureFlags) { 91 return new CallAudioRouteStateMachine(context, 92 callsManager, 93 bluetoothManager, 94 wiredHeadsetManager, 95 statusBarNotifier, 96 audioServiceFactory, 97 earpieceControl, 98 asyncTaskExecutor, 99 communicationDeviceTracker, 100 featureFlags); 101 } 102 } 103 /** Values for CallAudioRouteStateMachine constructor's earPieceRouting arg. */ 104 public static final int EARPIECE_FORCE_DISABLED = 0; 105 public static final int EARPIECE_FORCE_ENABLED = 1; 106 public static final int EARPIECE_AUTO_DETECT = 2; 107 108 /** Direct the audio stream through the device's earpiece. */ 109 public static final int ROUTE_EARPIECE = CallAudioState.ROUTE_EARPIECE; 110 111 /** Direct the audio stream through Bluetooth. */ 112 public static final int ROUTE_BLUETOOTH = CallAudioState.ROUTE_BLUETOOTH; 113 114 /** Direct the audio stream through a wired headset. */ 115 public static final int ROUTE_WIRED_HEADSET = CallAudioState.ROUTE_WIRED_HEADSET; 116 117 /** Direct the audio stream through the device's speakerphone. */ 118 public static final int ROUTE_SPEAKER = CallAudioState.ROUTE_SPEAKER; 119 120 /** Direct the audio stream through another device. */ 121 public static final int ROUTE_STREAMING = CallAudioState.ROUTE_STREAMING; 122 123 /** Valid values for the first argument for SWITCH_BASELINE_ROUTE */ 124 public static final int NO_INCLUDE_BLUETOOTH_IN_BASELINE = 0; 125 public static final int INCLUDE_BLUETOOTH_IN_BASELINE = 1; 126 127 @VisibleForTesting 128 public static final SparseArray<String> AUDIO_ROUTE_TO_LOG_EVENT = new SparseArray<String>() {{ 129 put(CallAudioState.ROUTE_BLUETOOTH, LogUtils.Events.AUDIO_ROUTE_BT); 130 put(CallAudioState.ROUTE_EARPIECE, LogUtils.Events.AUDIO_ROUTE_EARPIECE); 131 put(CallAudioState.ROUTE_SPEAKER, LogUtils.Events.AUDIO_ROUTE_SPEAKER); 132 put(CallAudioState.ROUTE_WIRED_HEADSET, LogUtils.Events.AUDIO_ROUTE_HEADSET); 133 }}; 134 135 private static final String ACTIVE_EARPIECE_ROUTE_NAME = "ActiveEarpieceRoute"; 136 private static final String ACTIVE_BLUETOOTH_ROUTE_NAME = "ActiveBluetoothRoute"; 137 private static final String ACTIVE_SPEAKER_ROUTE_NAME = "ActiveSpeakerRoute"; 138 private static final String ACTIVE_HEADSET_ROUTE_NAME = "ActiveHeadsetRoute"; 139 private static final String RINGING_BLUETOOTH_ROUTE_NAME = "RingingBluetoothRoute"; 140 private static final String QUIESCENT_EARPIECE_ROUTE_NAME = "QuiescentEarpieceRoute"; 141 private static final String QUIESCENT_BLUETOOTH_ROUTE_NAME = "QuiescentBluetoothRoute"; 142 private static final String QUIESCENT_SPEAKER_ROUTE_NAME = "QuiescentSpeakerRoute"; 143 private static final String QUIESCENT_HEADSET_ROUTE_NAME = "QuiescentHeadsetRoute"; 144 145 public static final String NAME = CallAudioRouteStateMachine.class.getName(); 146 147 @Override onPreHandleMessage(Message msg)148 protected void onPreHandleMessage(Message msg) { 149 if (msg.obj != null && msg.obj instanceof SomeArgs) { 150 Session session = (Session) ((SomeArgs) msg.obj).arg1; 151 String messageCodeName = MESSAGE_CODE_TO_NAME.get(msg.what, "unknown"); 152 Log.continueSession(session, "CARSM.pM_" + messageCodeName); 153 Log.i(this, "Message received: %s=%d, arg1=%d", messageCodeName, msg.what, msg.arg1); 154 } 155 } 156 157 @Override onPostHandleMessage(Message msg)158 protected void onPostHandleMessage(Message msg) { 159 Log.endSession(); 160 if (msg.obj != null && msg.obj instanceof SomeArgs) { 161 ((SomeArgs) msg.obj).recycle(); 162 } 163 } 164 165 abstract class AudioState extends State { 166 @Override enter()167 public void enter() { 168 super.enter(); 169 Log.addEvent(mCallsManager.getForegroundCall(), LogUtils.Events.AUDIO_ROUTE, 170 "Entering state " + getName()); 171 if (isActive()) { 172 Log.addEvent(mCallsManager.getForegroundCall(), 173 AUDIO_ROUTE_TO_LOG_EVENT.get(getRouteCode(), LogUtils.Events.AUDIO_ROUTE)); 174 } 175 } 176 177 @Override exit()178 public void exit() { 179 Log.addEvent(mCallsManager.getForegroundCall(), LogUtils.Events.AUDIO_ROUTE, 180 "Leaving state " + getName()); 181 super.exit(); 182 } 183 184 @Override processMessage(Message msg)185 public boolean processMessage(Message msg) { 186 int addedRoutes = 0; 187 int removedRoutes = 0; 188 boolean isHandled = NOT_HANDLED; 189 190 Log.i(this, "Processing message %s", 191 MESSAGE_CODE_TO_NAME.get(msg.what, Integer.toString(msg.what))); 192 switch (msg.what) { 193 case CONNECT_WIRED_HEADSET: 194 Log.addEvent(mCallsManager.getForegroundCall(), LogUtils.Events.AUDIO_ROUTE, 195 "Wired headset connected"); 196 removedRoutes |= ROUTE_EARPIECE; 197 addedRoutes |= ROUTE_WIRED_HEADSET; 198 break; 199 case DISCONNECT_WIRED_HEADSET: 200 Log.addEvent(mCallsManager.getForegroundCall(), LogUtils.Events.AUDIO_ROUTE, 201 "Wired headset disconnected"); 202 removedRoutes |= ROUTE_WIRED_HEADSET; 203 if (mDoesDeviceSupportEarpieceRoute) { 204 addedRoutes |= ROUTE_EARPIECE; 205 } 206 break; 207 case BT_ACTIVE_DEVICE_PRESENT: 208 Log.addEvent(mCallsManager.getForegroundCall(), LogUtils.Events.AUDIO_ROUTE, 209 "Bluetooth active device present"); 210 break; 211 case BT_ACTIVE_DEVICE_GONE: 212 Log.addEvent(mCallsManager.getForegroundCall(), LogUtils.Events.AUDIO_ROUTE, 213 "Bluetooth active device gone"); 214 break; 215 case BLUETOOTH_DEVICE_LIST_CHANGED: 216 Log.addEvent(mCallsManager.getForegroundCall(), LogUtils.Events.AUDIO_ROUTE, 217 "Bluetooth device list changed"); 218 Collection<BluetoothDevice> connectedDevices = 219 mBluetoothRouteManager.getConnectedDevices(); 220 if (connectedDevices.size() > 0) { 221 addedRoutes |= ROUTE_BLUETOOTH; 222 } else { 223 removedRoutes |= ROUTE_BLUETOOTH; 224 } 225 isHandled = HANDLED; 226 break; 227 case SWITCH_BASELINE_ROUTE: 228 sendInternalMessage(calculateBaselineRouteMessage(false, 229 msg.arg1 == INCLUDE_BLUETOOTH_IN_BASELINE)); 230 return HANDLED; 231 case USER_SWITCH_BASELINE_ROUTE: 232 sendInternalMessage(calculateBaselineRouteMessage(true, 233 msg.arg1 == INCLUDE_BLUETOOTH_IN_BASELINE)); 234 return HANDLED; 235 case USER_SWITCH_BLUETOOTH: 236 // If the user tries to switch to BT, reset the explicitly-switched-away flag. 237 mHasUserExplicitlyLeftBluetooth = false; 238 return NOT_HANDLED; 239 case SWITCH_FOCUS: 240 // Perform BT hearing aid active device caching/restoration 241 if (mAudioFocusType != NO_FOCUS && msg.arg1 == NO_FOCUS) { 242 mBluetoothRouteManager.restoreHearingAidDevice(); 243 } else if (mAudioFocusType == NO_FOCUS && msg.arg1 != NO_FOCUS) { 244 mBluetoothRouteManager.cacheHearingAidDevice(); 245 } 246 mAudioFocusType = msg.arg1; 247 return NOT_HANDLED; 248 default: 249 return NOT_HANDLED; 250 } 251 252 if (addedRoutes != 0 || removedRoutes != 0 253 || msg.what == BLUETOOTH_DEVICE_LIST_CHANGED) { 254 mAvailableRoutes = modifyRoutes(mAvailableRoutes, removedRoutes, addedRoutes, true); 255 mDeviceSupportedRoutes = modifyRoutes(mDeviceSupportedRoutes, removedRoutes, 256 addedRoutes, false); 257 updateSystemAudioState(); 258 } 259 260 return isHandled; 261 } 262 263 // Behavior will depend on whether the state is an active one or a quiescent one. updateSystemAudioState()264 abstract public void updateSystemAudioState(); isActive()265 abstract public boolean isActive(); getRouteCode()266 abstract public int getRouteCode(); 267 } 268 269 class ActiveEarpieceRoute extends EarpieceRoute { 270 @Override getName()271 public String getName() { 272 return ACTIVE_EARPIECE_ROUTE_NAME; 273 } 274 275 @Override isActive()276 public boolean isActive() { 277 return true; 278 } 279 280 @Override enter()281 public void enter() { 282 super.enter(); 283 setSpeakerphoneOn(false); 284 if (mFeatureFlags.callAudioCommunicationDeviceRefactor()) { 285 mCommunicationDeviceTracker.setCommunicationDevice( 286 AudioDeviceInfo.TYPE_BUILTIN_EARPIECE, null); 287 } 288 CallAudioState newState = new CallAudioState(mIsMuted, ROUTE_EARPIECE, 289 mAvailableRoutes, null, 290 mBluetoothRouteManager.getConnectedDevices()); 291 if (mFeatureFlags.earlyUpdateInternalCallAudioState()) { 292 updateInternalCallAudioState(); 293 setSystemAudioState(newState, true); 294 } else { 295 setSystemAudioState(newState, true); 296 updateInternalCallAudioState(); 297 } 298 } 299 300 @Override updateSystemAudioState()301 public void updateSystemAudioState() { 302 updateInternalCallAudioState(); 303 setSystemAudioState(mCurrentCallAudioState); 304 } 305 306 @Override processMessage(Message msg)307 public boolean processMessage(Message msg) { 308 if (super.processMessage(msg) == HANDLED) { 309 return HANDLED; 310 } 311 switch (msg.what) { 312 case SWITCH_EARPIECE: 313 case USER_SWITCH_EARPIECE: 314 case SPEAKER_OFF: 315 // Nothing to do here 316 return HANDLED; 317 case BT_AUDIO_CONNECTED: 318 transitionTo(mActiveBluetoothRoute); 319 return HANDLED; 320 case SWITCH_BLUETOOTH: 321 case USER_SWITCH_BLUETOOTH: 322 if ((mAvailableRoutes & ROUTE_BLUETOOTH) != 0) { 323 if (mFeatureFlags.callAudioCommunicationDeviceRefactor()) { 324 mCommunicationDeviceTracker.clearCommunicationDevice( 325 AudioDeviceInfo.TYPE_BUILTIN_EARPIECE); 326 } 327 if (mAudioFocusType == ACTIVE_FOCUS 328 || mBluetoothRouteManager.isInbandRingingEnabled()) { 329 String address = (msg.obj instanceof SomeArgs) ? 330 (String) ((SomeArgs) msg.obj).arg2 : null; 331 // Omit transition to ActiveBluetoothRoute 332 setBluetoothOn(address); 333 } else { 334 transitionTo(mRingingBluetoothRoute); 335 } 336 } else { 337 Log.w(this, "Ignoring switch to bluetooth command. Not available."); 338 } 339 return HANDLED; 340 case SWITCH_HEADSET: 341 case USER_SWITCH_HEADSET: 342 if ((mAvailableRoutes & ROUTE_WIRED_HEADSET) != 0) { 343 if (mFeatureFlags.callAudioCommunicationDeviceRefactor()) { 344 mCommunicationDeviceTracker.clearCommunicationDevice( 345 AudioDeviceInfo.TYPE_BUILTIN_EARPIECE); 346 } 347 transitionTo(mActiveHeadsetRoute); 348 } else { 349 Log.w(this, "Ignoring switch to headset command. Not available."); 350 } 351 return HANDLED; 352 case CONNECT_DOCK: 353 // fall through; we want to switch to speaker mode when docked and in a call. 354 case SWITCH_SPEAKER: 355 case USER_SWITCH_SPEAKER: 356 if (mFeatureFlags.callAudioCommunicationDeviceRefactor()) { 357 mCommunicationDeviceTracker.clearCommunicationDevice( 358 AudioDeviceInfo.TYPE_BUILTIN_EARPIECE); 359 } 360 setSpeakerphoneOn(true); 361 // fall through 362 case SPEAKER_ON: 363 transitionTo(mActiveSpeakerRoute); 364 return HANDLED; 365 case SWITCH_FOCUS: 366 if (msg.arg1 == NO_FOCUS) { 367 reinitialize(); 368 mCallAudioManager.notifyAudioOperationsComplete(); 369 } 370 return HANDLED; 371 default: 372 return NOT_HANDLED; 373 } 374 } 375 } 376 377 class QuiescentEarpieceRoute extends EarpieceRoute { 378 @Override getName()379 public String getName() { 380 return QUIESCENT_EARPIECE_ROUTE_NAME; 381 } 382 383 @Override isActive()384 public boolean isActive() { 385 return false; 386 } 387 388 @Override enter()389 public void enter() { 390 super.enter(); 391 mHasUserExplicitlyLeftBluetooth = false; 392 updateInternalCallAudioState(); 393 } 394 395 @Override updateSystemAudioState()396 public void updateSystemAudioState() { 397 updateInternalCallAudioState(); 398 } 399 400 @Override processMessage(Message msg)401 public boolean processMessage(Message msg) { 402 if (super.processMessage(msg) == HANDLED) { 403 return HANDLED; 404 } 405 switch (msg.what) { 406 case SWITCH_EARPIECE: 407 case USER_SWITCH_EARPIECE: 408 case SPEAKER_ON: 409 // Ignore speakerphone state changes outside of calls. 410 case SPEAKER_OFF: 411 // Nothing to do here 412 return HANDLED; 413 case BT_AUDIO_CONNECTED: 414 Log.w(this, "BT Audio came on in quiescent earpiece route."); 415 transitionTo(mActiveBluetoothRoute); 416 return HANDLED; 417 case SWITCH_BLUETOOTH: 418 case USER_SWITCH_BLUETOOTH: 419 if ((mAvailableRoutes & ROUTE_BLUETOOTH) != 0) { 420 transitionTo(mQuiescentBluetoothRoute); 421 } else { 422 Log.w(this, "Ignoring switch to bluetooth command. Not available."); 423 } 424 return HANDLED; 425 case SWITCH_HEADSET: 426 case USER_SWITCH_HEADSET: 427 if ((mAvailableRoutes & ROUTE_WIRED_HEADSET) != 0) { 428 transitionTo(mQuiescentHeadsetRoute); 429 } else { 430 Log.w(this, "Ignoring switch to headset command. Not available."); 431 } 432 return HANDLED; 433 case CONNECT_DOCK: 434 // fall through; we want to go to the quiescent speaker route when out of a call 435 case SWITCH_SPEAKER: 436 case USER_SWITCH_SPEAKER: 437 transitionTo(mQuiescentSpeakerRoute); 438 return HANDLED; 439 case SWITCH_FOCUS: 440 if (msg.arg1 == ACTIVE_FOCUS || msg.arg1 == RINGING_FOCUS) { 441 transitionTo(mActiveEarpieceRoute); 442 } else { 443 mCallAudioManager.notifyAudioOperationsComplete(); 444 } 445 return HANDLED; 446 default: 447 return NOT_HANDLED; 448 } 449 } 450 } 451 452 abstract class EarpieceRoute extends AudioState { 453 @Override getRouteCode()454 public int getRouteCode() { 455 return CallAudioState.ROUTE_EARPIECE; 456 } 457 458 @Override processMessage(Message msg)459 public boolean processMessage(Message msg) { 460 if (super.processMessage(msg) == HANDLED) { 461 return HANDLED; 462 } 463 switch (msg.what) { 464 case CONNECT_WIRED_HEADSET: 465 sendInternalMessage(SWITCH_HEADSET); 466 return HANDLED; 467 case BT_ACTIVE_DEVICE_PRESENT: 468 if (!mHasUserExplicitlyLeftBluetooth) { 469 sendInternalMessage(SWITCH_BLUETOOTH); 470 } else { 471 Log.i(this, "Not switching to BT route from earpiece because user has " + 472 "explicitly disconnected."); 473 } 474 return HANDLED; 475 case BT_ACTIVE_DEVICE_GONE: 476 // No change in audio route required 477 return HANDLED; 478 case DISCONNECT_WIRED_HEADSET: 479 Log.e(this, new IllegalStateException(), 480 "Wired headset should not go from connected to not when on " + 481 "earpiece"); 482 return HANDLED; 483 case BT_AUDIO_DISCONNECTED: 484 // This may be sent as a confirmation by the BT stack after switch off BT. 485 return HANDLED; 486 case DISCONNECT_DOCK: 487 // Nothing to do here 488 return HANDLED; 489 case STREAMING_FORCE_ENABLED: 490 transitionTo(mStreamingState); 491 return HANDLED; 492 default: 493 return NOT_HANDLED; 494 } 495 } 496 } 497 498 class ActiveHeadsetRoute extends HeadsetRoute { 499 @Override getName()500 public String getName() { 501 return ACTIVE_HEADSET_ROUTE_NAME; 502 } 503 504 @Override isActive()505 public boolean isActive() { 506 return true; 507 } 508 509 @Override enter()510 public void enter() { 511 super.enter(); 512 setSpeakerphoneOn(false); 513 if (mFeatureFlags.callAudioCommunicationDeviceRefactor()) { 514 mCommunicationDeviceTracker.setCommunicationDevice( 515 AudioDeviceInfo.TYPE_WIRED_HEADSET, null); 516 } 517 CallAudioState newState = new CallAudioState(mIsMuted, ROUTE_WIRED_HEADSET, 518 mAvailableRoutes, null, mBluetoothRouteManager.getConnectedDevices()); 519 if (mFeatureFlags.earlyUpdateInternalCallAudioState()) { 520 updateInternalCallAudioState(); 521 setSystemAudioState(newState, true); 522 } else { 523 setSystemAudioState(newState, true); 524 updateInternalCallAudioState(); 525 } 526 } 527 528 @Override updateSystemAudioState()529 public void updateSystemAudioState() { 530 updateInternalCallAudioState(); 531 setSystemAudioState(mCurrentCallAudioState); 532 } 533 534 @Override processMessage(Message msg)535 public boolean processMessage(Message msg) { 536 if (super.processMessage(msg) == HANDLED) { 537 return HANDLED; 538 } 539 switch (msg.what) { 540 case SWITCH_EARPIECE: 541 case USER_SWITCH_EARPIECE: 542 if ((mAvailableRoutes & ROUTE_EARPIECE) != 0) { 543 if (mFeatureFlags.callAudioCommunicationDeviceRefactor()) { 544 mCommunicationDeviceTracker.clearCommunicationDevice( 545 AudioDeviceInfo.TYPE_WIRED_HEADSET); 546 } 547 transitionTo(mActiveEarpieceRoute); 548 } else { 549 Log.w(this, "Ignoring switch to earpiece command. Not available."); 550 } 551 return HANDLED; 552 case BT_AUDIO_CONNECTED: 553 transitionTo(mActiveBluetoothRoute); 554 return HANDLED; 555 case SWITCH_BLUETOOTH: 556 case USER_SWITCH_BLUETOOTH: 557 if ((mAvailableRoutes & ROUTE_BLUETOOTH) != 0) { 558 if (mAudioFocusType == ACTIVE_FOCUS 559 || mBluetoothRouteManager.isInbandRingingEnabled()) { 560 String address = (msg.obj instanceof SomeArgs) ? 561 (String) ((SomeArgs) msg.obj).arg2 : null; 562 if (mFeatureFlags.callAudioCommunicationDeviceRefactor()) { 563 mCommunicationDeviceTracker.clearCommunicationDevice( 564 AudioDeviceInfo.TYPE_WIRED_HEADSET); 565 } 566 // Omit transition to ActiveBluetoothRoute until actual connection. 567 setBluetoothOn(address); 568 } else { 569 transitionTo(mRingingBluetoothRoute); 570 } 571 } else { 572 Log.w(this, "Ignoring switch to bluetooth command. Not available."); 573 } 574 return HANDLED; 575 case SWITCH_HEADSET: 576 case USER_SWITCH_HEADSET: 577 case SPEAKER_OFF: 578 // Nothing to do 579 return HANDLED; 580 case SWITCH_SPEAKER: 581 case USER_SWITCH_SPEAKER: 582 if (mFeatureFlags.callAudioCommunicationDeviceRefactor()) { 583 mCommunicationDeviceTracker.clearCommunicationDevice( 584 AudioDeviceInfo.TYPE_WIRED_HEADSET); 585 } 586 setSpeakerphoneOn(true); 587 // fall through 588 case SPEAKER_ON: 589 transitionTo(mActiveSpeakerRoute); 590 return HANDLED; 591 case SWITCH_FOCUS: 592 if (msg.arg1 == NO_FOCUS) { 593 reinitialize(); 594 mCallAudioManager.notifyAudioOperationsComplete(); 595 } 596 return HANDLED; 597 case STREAMING_FORCE_ENABLED: 598 transitionTo(mStreamingState); 599 return HANDLED; 600 default: 601 return NOT_HANDLED; 602 } 603 } 604 } 605 606 class QuiescentHeadsetRoute extends HeadsetRoute { 607 @Override getName()608 public String getName() { 609 return QUIESCENT_HEADSET_ROUTE_NAME; 610 } 611 612 @Override isActive()613 public boolean isActive() { 614 return false; 615 } 616 617 @Override enter()618 public void enter() { 619 super.enter(); 620 mHasUserExplicitlyLeftBluetooth = false; 621 updateInternalCallAudioState(); 622 } 623 624 @Override updateSystemAudioState()625 public void updateSystemAudioState() { 626 updateInternalCallAudioState(); 627 } 628 629 @Override processMessage(Message msg)630 public boolean processMessage(Message msg) { 631 if (super.processMessage(msg) == HANDLED) { 632 return HANDLED; 633 } 634 switch (msg.what) { 635 case SWITCH_EARPIECE: 636 case USER_SWITCH_EARPIECE: 637 if ((mAvailableRoutes & ROUTE_EARPIECE) != 0) { 638 transitionTo(mQuiescentEarpieceRoute); 639 } else { 640 Log.w(this, "Ignoring switch to earpiece command. Not available."); 641 } 642 return HANDLED; 643 case BT_AUDIO_CONNECTED: 644 transitionTo(mActiveBluetoothRoute); 645 Log.w(this, "BT Audio came on in quiescent headset route."); 646 return HANDLED; 647 case SWITCH_BLUETOOTH: 648 case USER_SWITCH_BLUETOOTH: 649 if ((mAvailableRoutes & ROUTE_BLUETOOTH) != 0) { 650 transitionTo(mQuiescentBluetoothRoute); 651 } else { 652 Log.w(this, "Ignoring switch to bluetooth command. Not available."); 653 } 654 return HANDLED; 655 case SWITCH_HEADSET: 656 case USER_SWITCH_HEADSET: 657 case SPEAKER_ON: 658 // Ignore speakerphone state changes outside of calls. 659 case SPEAKER_OFF: 660 // Nothing to do 661 return HANDLED; 662 case SWITCH_SPEAKER: 663 case USER_SWITCH_SPEAKER: 664 transitionTo(mQuiescentSpeakerRoute); 665 return HANDLED; 666 case SWITCH_FOCUS: 667 if (msg.arg1 == ACTIVE_FOCUS || msg.arg1 == RINGING_FOCUS) { 668 transitionTo(mActiveHeadsetRoute); 669 } else { 670 mCallAudioManager.notifyAudioOperationsComplete(); 671 } 672 return HANDLED; 673 default: 674 return NOT_HANDLED; 675 } 676 } 677 } 678 679 abstract class HeadsetRoute extends AudioState { 680 @Override getRouteCode()681 public int getRouteCode() { 682 return CallAudioState.ROUTE_WIRED_HEADSET; 683 } 684 685 @Override processMessage(Message msg)686 public boolean processMessage(Message msg) { 687 if (super.processMessage(msg) == HANDLED) { 688 return HANDLED; 689 } 690 switch (msg.what) { 691 case CONNECT_WIRED_HEADSET: 692 Log.e(this, new IllegalStateException(), 693 "Wired headset should already be connected."); 694 return HANDLED; 695 case BT_ACTIVE_DEVICE_PRESENT: 696 if (!mHasUserExplicitlyLeftBluetooth) { 697 sendInternalMessage(SWITCH_BLUETOOTH); 698 } else { 699 Log.i(this, "Not switching to BT route from headset because user has " + 700 "explicitly disconnected."); 701 } 702 return HANDLED; 703 case BT_ACTIVE_DEVICE_GONE: 704 // No change in audio route required 705 return HANDLED; 706 case DISCONNECT_WIRED_HEADSET: 707 if (mWasOnSpeaker) { 708 setSpeakerphoneOn(true); 709 sendInternalMessage(SWITCH_SPEAKER); 710 } else { 711 sendInternalMessage(SWITCH_BASELINE_ROUTE, INCLUDE_BLUETOOTH_IN_BASELINE); 712 } 713 return HANDLED; 714 case BT_AUDIO_DISCONNECTED: 715 // This may be sent as a confirmation by the BT stack after switch off BT. 716 return HANDLED; 717 case CONNECT_DOCK: 718 // Nothing to do here 719 return HANDLED; 720 case DISCONNECT_DOCK: 721 // Nothing to do here 722 return HANDLED; 723 default: 724 return NOT_HANDLED; 725 } 726 } 727 } 728 729 // Note: transitions to/from this class work a bit differently -- we delegate to 730 // BluetoothRouteManager to manage all Bluetooth state, so instead of transitioning to one of 731 // the bluetooth states immediately when there's an request to do so, we wait for 732 // BluetoothRouteManager to report its state before we go into this state. 733 class ActiveBluetoothRoute extends BluetoothRoute { 734 @Override getName()735 public String getName() { 736 return ACTIVE_BLUETOOTH_ROUTE_NAME; 737 } 738 739 @Override isActive()740 public boolean isActive() { 741 return true; 742 } 743 744 @Override enter()745 public void enter() { 746 super.enter(); 747 setSpeakerphoneOn(false); 748 // Try arbitrarily connecting to BT audio if we haven't already. This handles 749 // the edge case of when the audio route is in a quiescent route while in-call and 750 // the BT connection fails to be set. Previously, the logic was to setBluetoothOn in 751 // ACTIVE_FOCUS but the route would still remain in a quiescent route, so instead we 752 // should be transitioning directly into the active route. 753 if (mFeatureFlags.callAudioCommunicationDeviceRefactor()) { 754 setBluetoothOn(null); 755 } 756 if (mFeatureFlags.updateRouteMaskWhenBtConnected()) { 757 mAvailableRoutes |= ROUTE_BLUETOOTH; 758 } 759 CallAudioState newState = new CallAudioState(mIsMuted, ROUTE_BLUETOOTH, 760 mAvailableRoutes, mBluetoothRouteManager.getBluetoothAudioConnectedDevice(), 761 mBluetoothRouteManager.getConnectedDevices()); 762 if (mFeatureFlags.earlyUpdateInternalCallAudioState()) { 763 updateInternalCallAudioState(); 764 setSystemAudioState(newState, true); 765 } else { 766 setSystemAudioState(newState, true); 767 updateInternalCallAudioState(); 768 } 769 // Do not send RINGER_MODE_CHANGE if no Bluetooth SCO audio device is available 770 if (mBluetoothRouteManager.getBluetoothAudioConnectedDevice() != null) { 771 mCallAudioManager.onRingerModeChange(); 772 } 773 } 774 775 @Override updateSystemAudioState()776 public void updateSystemAudioState() { 777 updateInternalCallAudioState(); 778 setSystemAudioState(mCurrentCallAudioState); 779 } 780 781 @Override handleBtInitiatedDisconnect()782 public void handleBtInitiatedDisconnect() { 783 // There's special-case state transitioning here -- if BT tells us that 784 // something got disconnected, we don't want to disconnect BT before 785 // transitioning, since BT might be trying to connect another device in the 786 // meantime. 787 int command = calculateBaselineRouteMessage(false, false); 788 switch (command) { 789 case SWITCH_EARPIECE: 790 transitionTo(mActiveEarpieceRoute); 791 break; 792 case SWITCH_HEADSET: 793 transitionTo(mActiveHeadsetRoute); 794 break; 795 case SWITCH_SPEAKER: 796 setSpeakerphoneOn(true); 797 transitionTo(mActiveSpeakerRoute); 798 break; 799 default: 800 Log.w(this, "Got unexpected code " + command + " when processing a" 801 + " BT-initiated audio disconnect"); 802 // Some fallback logic to make sure we make it off the bluetooth route. 803 super.handleBtInitiatedDisconnect(); 804 break; 805 } 806 } 807 808 @Override processMessage(Message msg)809 public boolean processMessage(Message msg) { 810 if (super.processMessage(msg) == HANDLED) { 811 return HANDLED; 812 } 813 switch (msg.what) { 814 case USER_SWITCH_EARPIECE: 815 mHasUserExplicitlyLeftBluetooth = true; 816 // fall through 817 case SWITCH_EARPIECE: 818 if ((mAvailableRoutes & ROUTE_EARPIECE) != 0) { 819 setBluetoothOff(); 820 transitionTo(mActiveEarpieceRoute); 821 } else { 822 Log.w(this, "Ignoring switch to earpiece command. Not available."); 823 } 824 return HANDLED; 825 case BT_AUDIO_CONNECTED: 826 // Send ringer mode change because we transit to ActiveBluetoothState even 827 // when HFP is connecting 828 mCallAudioManager.onRingerModeChange(); 829 // Update the in-call app on the new active BT device in case that changed. 830 updateSystemAudioState(); 831 return HANDLED; 832 case SWITCH_BLUETOOTH: 833 case USER_SWITCH_BLUETOOTH: 834 String address = (msg.obj instanceof SomeArgs) ? 835 (String) ((SomeArgs) msg.obj).arg2 : null; 836 setBluetoothOn(address); 837 return HANDLED; 838 case USER_SWITCH_HEADSET: 839 mHasUserExplicitlyLeftBluetooth = true; 840 // fall through 841 case SWITCH_HEADSET: 842 if ((mAvailableRoutes & ROUTE_WIRED_HEADSET) != 0) { 843 setBluetoothOff(); 844 transitionTo(mActiveHeadsetRoute); 845 } else { 846 Log.w(this, "Ignoring switch to headset command. Not available."); 847 } 848 return HANDLED; 849 case USER_SWITCH_SPEAKER: 850 mHasUserExplicitlyLeftBluetooth = true; 851 // fall through 852 case SWITCH_SPEAKER: 853 setSpeakerphoneOn(true); 854 // fall through 855 case SPEAKER_ON: 856 setBluetoothOff(); 857 transitionTo(mActiveSpeakerRoute); 858 return HANDLED; 859 case SPEAKER_OFF: 860 return HANDLED; 861 case SWITCH_FOCUS: 862 if (msg.arg1 == NO_FOCUS) { 863 // Only disconnect audio here instead of routing away from BT entirely. 864 if (mFeatureFlags.transitRouteBeforeAudioDisconnectBt()) { 865 // Note: We have to turn off mute here rather than when entering the 866 // QuiescentBluetooth route because setMuteOn will only work when there the 867 // current state is active. 868 // We don't need to do this in the unflagged path since reinitialize 869 // will turn off mute. 870 if (mFeatureFlags.resetMuteWhenEnteringQuiescentBtRoute()) { 871 setMuteOn(false); 872 } 873 transitionTo(mQuiescentBluetoothRoute); 874 mBluetoothRouteManager.disconnectAudio(); 875 } else { 876 mBluetoothRouteManager.disconnectAudio(); 877 reinitialize(); 878 } 879 mCallAudioManager.notifyAudioOperationsComplete(); 880 } else if (msg.arg1 == RINGING_FOCUS 881 && !mBluetoothRouteManager.isInbandRingingEnabled()) { 882 setBluetoothOff(); 883 transitionTo(mRingingBluetoothRoute); 884 } 885 return HANDLED; 886 case BT_AUDIO_DISCONNECTED: 887 handleBtInitiatedDisconnect(); 888 return HANDLED; 889 default: 890 return NOT_HANDLED; 891 } 892 } 893 } 894 895 // This state is only used when the device doesn't support in-band ring. If it does, 896 // ActiveBluetoothRoute is used instead. 897 class RingingBluetoothRoute extends BluetoothRoute { 898 @Override getName()899 public String getName() { 900 return RINGING_BLUETOOTH_ROUTE_NAME; 901 } 902 903 @Override isActive()904 public boolean isActive() { 905 return false; 906 } 907 908 @Override enter()909 public void enter() { 910 super.enter(); 911 setSpeakerphoneOn(false); 912 // Do not enable SCO audio here, since RING is being sent to the headset. 913 CallAudioState newState = new CallAudioState(mIsMuted, ROUTE_BLUETOOTH, 914 mAvailableRoutes, mBluetoothRouteManager.getBluetoothAudioConnectedDevice(), 915 mBluetoothRouteManager.getConnectedDevices()); 916 if (mFeatureFlags.earlyUpdateInternalCallAudioState()) { 917 updateInternalCallAudioState(); 918 setSystemAudioState(newState, true); 919 } else { 920 setSystemAudioState(newState, true); 921 updateInternalCallAudioState(); 922 } 923 } 924 925 @Override updateSystemAudioState()926 public void updateSystemAudioState() { 927 updateInternalCallAudioState(); 928 setSystemAudioState(mCurrentCallAudioState); 929 } 930 931 @Override processMessage(Message msg)932 public boolean processMessage(Message msg) { 933 if (super.processMessage(msg) == HANDLED) { 934 return HANDLED; 935 } 936 switch (msg.what) { 937 case USER_SWITCH_EARPIECE: 938 mHasUserExplicitlyLeftBluetooth = true; 939 // fall through 940 case SWITCH_EARPIECE: 941 if ((mAvailableRoutes & ROUTE_EARPIECE) != 0) { 942 transitionTo(mActiveEarpieceRoute); 943 } else { 944 Log.w(this, "Ignoring switch to earpiece command. Not available."); 945 } 946 return HANDLED; 947 case BT_AUDIO_CONNECTED: 948 transitionTo(mActiveBluetoothRoute); 949 return HANDLED; 950 case SWITCH_BLUETOOTH: 951 case USER_SWITCH_BLUETOOTH: 952 // Nothing to do 953 return HANDLED; 954 case USER_SWITCH_HEADSET: 955 mHasUserExplicitlyLeftBluetooth = true; 956 // fall through 957 case SWITCH_HEADSET: 958 if ((mAvailableRoutes & ROUTE_WIRED_HEADSET) != 0) { 959 transitionTo(mActiveHeadsetRoute); 960 } else { 961 Log.w(this, "Ignoring switch to headset command. Not available."); 962 } 963 return HANDLED; 964 case USER_SWITCH_SPEAKER: 965 mHasUserExplicitlyLeftBluetooth = true; 966 // fall through 967 case SWITCH_SPEAKER: 968 setSpeakerphoneOn(true); 969 // fall through 970 case SPEAKER_ON: 971 transitionTo(mActiveSpeakerRoute); 972 return HANDLED; 973 case SPEAKER_OFF: 974 return HANDLED; 975 case SWITCH_FOCUS: 976 if (msg.arg1 == NO_FOCUS) { 977 reinitialize(); 978 mCallAudioManager.notifyAudioOperationsComplete(); 979 } else if (msg.arg1 == ACTIVE_FOCUS) { 980 setBluetoothOn(null); 981 } 982 return HANDLED; 983 case BT_AUDIO_DISCONNECTED: 984 // Ignore this -- audio disconnecting while ringing w/o in-band should not 985 // cause a route switch, since the device is still connected. 986 return HANDLED; 987 default: 988 return NOT_HANDLED; 989 } 990 } 991 } 992 993 class QuiescentBluetoothRoute extends BluetoothRoute { 994 @Override getName()995 public String getName() { 996 return QUIESCENT_BLUETOOTH_ROUTE_NAME; 997 } 998 999 @Override isActive()1000 public boolean isActive() { 1001 return false; 1002 } 1003 1004 @Override enter()1005 public void enter() { 1006 super.enter(); 1007 mHasUserExplicitlyLeftBluetooth = false; 1008 updateInternalCallAudioState(); 1009 } 1010 1011 @Override updateSystemAudioState()1012 public void updateSystemAudioState() { 1013 updateInternalCallAudioState(); 1014 } 1015 1016 @Override processMessage(Message msg)1017 public boolean processMessage(Message msg) { 1018 if (super.processMessage(msg) == HANDLED) { 1019 return HANDLED; 1020 } 1021 switch (msg.what) { 1022 case SWITCH_EARPIECE: 1023 case USER_SWITCH_EARPIECE: 1024 if ((mAvailableRoutes & ROUTE_EARPIECE) != 0) { 1025 transitionTo(mQuiescentEarpieceRoute); 1026 } else { 1027 Log.w(this, "Ignoring switch to earpiece command. Not available."); 1028 } 1029 return HANDLED; 1030 case BT_AUDIO_CONNECTED: 1031 transitionTo(mActiveBluetoothRoute); 1032 return HANDLED; 1033 case SWITCH_BLUETOOTH: 1034 case USER_SWITCH_BLUETOOTH: 1035 case SPEAKER_ON: 1036 // Ignore speakerphone state changes outside of calls. 1037 case SPEAKER_OFF: 1038 // Nothing to do 1039 return HANDLED; 1040 case SWITCH_HEADSET: 1041 case USER_SWITCH_HEADSET: 1042 if ((mAvailableRoutes & ROUTE_WIRED_HEADSET) != 0) { 1043 transitionTo(mQuiescentHeadsetRoute); 1044 } else { 1045 Log.w(this, "Ignoring switch to headset command. Not available."); 1046 } 1047 return HANDLED; 1048 case SWITCH_SPEAKER: 1049 case USER_SWITCH_SPEAKER: 1050 transitionTo(mQuiescentSpeakerRoute); 1051 return HANDLED; 1052 case SWITCH_FOCUS: 1053 if (msg.arg1 == ACTIVE_FOCUS) { 1054 // It is possible that the connection to BT will fail while in-call, in 1055 // which case, we want to transition into the active route. 1056 if (mFeatureFlags.callAudioCommunicationDeviceRefactor()) { 1057 transitionTo(mActiveBluetoothRoute); 1058 } else { 1059 setBluetoothOn(null); 1060 } 1061 } else if (msg.arg1 == RINGING_FOCUS) { 1062 if (mBluetoothRouteManager.isInbandRingingEnabled()) { 1063 setBluetoothOn(null); 1064 } else { 1065 transitionTo(mRingingBluetoothRoute); 1066 } 1067 } else { 1068 mCallAudioManager.notifyAudioOperationsComplete(); 1069 } 1070 return HANDLED; 1071 case BT_AUDIO_DISCONNECTED: 1072 // Ignore this -- audio disconnecting while quiescent should not cause a 1073 // route switch, since the device is still connected. 1074 return HANDLED; 1075 default: 1076 return NOT_HANDLED; 1077 } 1078 } 1079 } 1080 1081 abstract class BluetoothRoute extends AudioState { 1082 @Override getRouteCode()1083 public int getRouteCode() { 1084 return CallAudioState.ROUTE_BLUETOOTH; 1085 } 1086 handleBtInitiatedDisconnect()1087 public void handleBtInitiatedDisconnect() { 1088 sendInternalMessage(SWITCH_BASELINE_ROUTE, NO_INCLUDE_BLUETOOTH_IN_BASELINE); 1089 } 1090 1091 @Override processMessage(Message msg)1092 public boolean processMessage(Message msg) { 1093 if (super.processMessage(msg) == HANDLED) { 1094 return HANDLED; 1095 } 1096 switch (msg.what) { 1097 case CONNECT_WIRED_HEADSET: 1098 sendInternalMessage(SWITCH_HEADSET); 1099 return HANDLED; 1100 case BT_ACTIVE_DEVICE_PRESENT: 1101 Log.w(this, "Bluetooth active device should not" 1102 + " have been null while we were in BT route."); 1103 return HANDLED; 1104 case BT_ACTIVE_DEVICE_GONE: 1105 handleBtInitiatedDisconnect(); 1106 mWasOnSpeaker = false; 1107 return HANDLED; 1108 case DISCONNECT_WIRED_HEADSET: 1109 // No change in audio route required 1110 return HANDLED; 1111 case CONNECT_DOCK: 1112 // Nothing to do here 1113 return HANDLED; 1114 case DISCONNECT_DOCK: 1115 // Nothing to do here 1116 return HANDLED; 1117 case STREAMING_FORCE_ENABLED: 1118 transitionTo(mStreamingState); 1119 return HANDLED; 1120 default: 1121 return NOT_HANDLED; 1122 } 1123 } 1124 } 1125 1126 class ActiveSpeakerRoute extends SpeakerRoute { 1127 @Override getName()1128 public String getName() { 1129 return ACTIVE_SPEAKER_ROUTE_NAME; 1130 } 1131 1132 @Override isActive()1133 public boolean isActive() { 1134 return true; 1135 } 1136 1137 @Override enter()1138 public void enter() { 1139 super.enter(); 1140 // Don't set speakerphone on here -- we might end up in this state by following 1141 // the speaker state that some other app commanded. 1142 mWasOnSpeaker = true; 1143 CallAudioState newState = new CallAudioState(mIsMuted, ROUTE_SPEAKER, 1144 mAvailableRoutes, null, mBluetoothRouteManager.getConnectedDevices()); 1145 if (mFeatureFlags.earlyUpdateInternalCallAudioState()) { 1146 updateInternalCallAudioState(); 1147 setSystemAudioState(newState, true); 1148 } else { 1149 setSystemAudioState(newState, true); 1150 updateInternalCallAudioState(); 1151 } 1152 } 1153 1154 @Override updateSystemAudioState()1155 public void updateSystemAudioState() { 1156 updateInternalCallAudioState(); 1157 setSystemAudioState(mCurrentCallAudioState); 1158 } 1159 1160 @Override processMessage(Message msg)1161 public boolean processMessage(Message msg) { 1162 if (super.processMessage(msg) == HANDLED) { 1163 return HANDLED; 1164 } 1165 switch(msg.what) { 1166 case USER_SWITCH_EARPIECE: 1167 mWasOnSpeaker = false; 1168 // fall through 1169 case SWITCH_EARPIECE: 1170 if ((mAvailableRoutes & ROUTE_EARPIECE) != 0) { 1171 transitionTo(mActiveEarpieceRoute); 1172 } else { 1173 Log.w(this, "Ignoring switch to earpiece command. Not available."); 1174 } 1175 return HANDLED; 1176 case BT_AUDIO_CONNECTED: 1177 transitionTo(mActiveBluetoothRoute); 1178 return HANDLED; 1179 case USER_SWITCH_BLUETOOTH: 1180 mWasOnSpeaker = false; 1181 // fall through 1182 case SWITCH_BLUETOOTH: 1183 String address = (msg.obj instanceof SomeArgs) ? 1184 (String) ((SomeArgs) msg.obj).arg2 : null; 1185 if ((mAvailableRoutes & ROUTE_BLUETOOTH) != 0) { 1186 if (mAudioFocusType == ACTIVE_FOCUS 1187 || mBluetoothRouteManager.isInbandRingingEnabled()) { 1188 // Omit transition to ActiveBluetoothRoute 1189 setBluetoothOn(address); 1190 } else { 1191 transitionTo(mRingingBluetoothRoute); 1192 } 1193 } else { 1194 Log.w(this, "Ignoring switch to bluetooth command. Not available."); 1195 } 1196 return HANDLED; 1197 case USER_SWITCH_HEADSET: 1198 mWasOnSpeaker = false; 1199 // fall through 1200 case SWITCH_HEADSET: 1201 if ((mAvailableRoutes & ROUTE_WIRED_HEADSET) != 0) { 1202 transitionTo(mActiveHeadsetRoute); 1203 } else { 1204 Log.w(this, "Ignoring switch to headset command. Not available."); 1205 } 1206 return HANDLED; 1207 case SWITCH_SPEAKER: 1208 case USER_SWITCH_SPEAKER: 1209 // Nothing to do 1210 return HANDLED; 1211 case SPEAKER_ON: 1212 // Expected, since we just transitioned here 1213 return HANDLED; 1214 case SPEAKER_OFF: 1215 // Check if we already requested to connect to other devices and just waiting 1216 // for their response. In some cases, this SPEAKER_OFF message may come in 1217 // before the response, we can just ignore the message here to not re-evaluate 1218 // the baseline route incorrectly 1219 if (!mBluetoothRouteManager.isBluetoothAudioConnectedOrPending()) { 1220 sendInternalMessage(SWITCH_BASELINE_ROUTE, INCLUDE_BLUETOOTH_IN_BASELINE); 1221 } 1222 return HANDLED; 1223 case SWITCH_FOCUS: 1224 if (msg.arg1 == NO_FOCUS) { 1225 reinitialize(); 1226 mCallAudioManager.notifyAudioOperationsComplete(); 1227 } 1228 return HANDLED; 1229 default: 1230 return NOT_HANDLED; 1231 } 1232 } 1233 } 1234 1235 class QuiescentSpeakerRoute extends SpeakerRoute { 1236 @Override getName()1237 public String getName() { 1238 return QUIESCENT_SPEAKER_ROUTE_NAME; 1239 } 1240 1241 @Override isActive()1242 public boolean isActive() { 1243 return false; 1244 } 1245 1246 @Override enter()1247 public void enter() { 1248 super.enter(); 1249 mHasUserExplicitlyLeftBluetooth = false; 1250 // Omit setting mWasOnSpeaker to true here, since this does not reflect a call 1251 // actually being on speakerphone. 1252 updateInternalCallAudioState(); 1253 } 1254 1255 @Override updateSystemAudioState()1256 public void updateSystemAudioState() { 1257 updateInternalCallAudioState(); 1258 } 1259 1260 @Override processMessage(Message msg)1261 public boolean processMessage(Message msg) { 1262 if (super.processMessage(msg) == HANDLED) { 1263 return HANDLED; 1264 } 1265 switch(msg.what) { 1266 case SWITCH_EARPIECE: 1267 case USER_SWITCH_EARPIECE: 1268 if ((mAvailableRoutes & ROUTE_EARPIECE) != 0) { 1269 transitionTo(mQuiescentEarpieceRoute); 1270 } else { 1271 Log.w(this, "Ignoring switch to earpiece command. Not available."); 1272 } 1273 return HANDLED; 1274 case BT_AUDIO_CONNECTED: 1275 transitionTo(mActiveBluetoothRoute); 1276 Log.w(this, "BT audio reported as connected while in quiescent speaker"); 1277 return HANDLED; 1278 case SWITCH_BLUETOOTH: 1279 case USER_SWITCH_BLUETOOTH: 1280 if ((mAvailableRoutes & ROUTE_BLUETOOTH) != 0) { 1281 transitionTo(mQuiescentBluetoothRoute); 1282 } else { 1283 Log.w(this, "Ignoring switch to bluetooth command. Not available."); 1284 } 1285 return HANDLED; 1286 case SWITCH_HEADSET: 1287 case USER_SWITCH_HEADSET: 1288 if ((mAvailableRoutes & ROUTE_WIRED_HEADSET) != 0) { 1289 transitionTo(mQuiescentHeadsetRoute); 1290 } else { 1291 Log.w(this, "Ignoring switch to headset command. Not available."); 1292 } 1293 return HANDLED; 1294 case SWITCH_SPEAKER: 1295 case USER_SWITCH_SPEAKER: 1296 case SPEAKER_ON: 1297 // Nothing to do 1298 return HANDLED; 1299 case DISCONNECT_DOCK: 1300 // Fall-through; same as if speaker goes off, we want to switch baseline. 1301 case SPEAKER_OFF: 1302 sendInternalMessage(SWITCH_BASELINE_ROUTE, INCLUDE_BLUETOOTH_IN_BASELINE); 1303 return HANDLED; 1304 case SWITCH_FOCUS: 1305 if (msg.arg1 == ACTIVE_FOCUS || msg.arg1 == RINGING_FOCUS) { 1306 setSpeakerphoneOn(true); 1307 transitionTo(mActiveSpeakerRoute); 1308 } else { 1309 mCallAudioManager.notifyAudioOperationsComplete(); 1310 } 1311 return HANDLED; 1312 default: 1313 return NOT_HANDLED; 1314 } 1315 } 1316 } 1317 1318 abstract class SpeakerRoute extends AudioState { 1319 @Override getRouteCode()1320 public int getRouteCode() { 1321 return CallAudioState.ROUTE_SPEAKER; 1322 } 1323 1324 @Override processMessage(Message msg)1325 public boolean processMessage(Message msg) { 1326 if (super.processMessage(msg) == HANDLED) { 1327 return HANDLED; 1328 } 1329 switch (msg.what) { 1330 case CONNECT_WIRED_HEADSET: 1331 sendInternalMessage(SWITCH_HEADSET); 1332 return HANDLED; 1333 case BT_ACTIVE_DEVICE_PRESENT: 1334 if (!mHasUserExplicitlyLeftBluetooth) { 1335 sendInternalMessage(SWITCH_BLUETOOTH); 1336 } else { 1337 Log.i(this, "Not switching to BT route from speaker because user has " + 1338 "explicitly disconnected."); 1339 } 1340 return HANDLED; 1341 case BT_ACTIVE_DEVICE_GONE: 1342 // No change in audio route required 1343 return HANDLED; 1344 case DISCONNECT_WIRED_HEADSET: 1345 // No change in audio route required 1346 return HANDLED; 1347 case BT_AUDIO_DISCONNECTED: 1348 // This may be sent as a confirmation by the BT stack after switch off BT. 1349 return HANDLED; 1350 case CONNECT_DOCK: 1351 // Nothing to do here 1352 return HANDLED; 1353 case DISCONNECT_DOCK: 1354 sendInternalMessage(SWITCH_BASELINE_ROUTE, INCLUDE_BLUETOOTH_IN_BASELINE); 1355 return HANDLED; 1356 case STREAMING_FORCE_ENABLED: 1357 transitionTo(mStreamingState); 1358 return HANDLED; 1359 default: 1360 return NOT_HANDLED; 1361 } 1362 } 1363 } 1364 1365 class StreamingState extends AudioState { 1366 @Override enter()1367 public void enter() { 1368 super.enter(); 1369 updateSystemAudioState(); 1370 } 1371 1372 @Override updateSystemAudioState()1373 public void updateSystemAudioState() { 1374 updateInternalCallAudioState(); 1375 setSystemAudioState(mCurrentCallAudioState); 1376 } 1377 1378 @Override isActive()1379 public boolean isActive() { 1380 return true; 1381 } 1382 1383 @Override getRouteCode()1384 public int getRouteCode() { 1385 return CallAudioState.ROUTE_STREAMING; 1386 } 1387 1388 @Override processMessage(Message msg)1389 public boolean processMessage(Message msg) { 1390 if (super.processMessage(msg) == HANDLED) { 1391 return HANDLED; 1392 } 1393 switch (msg.what) { 1394 case SWITCH_EARPIECE: 1395 case USER_SWITCH_EARPIECE: 1396 case SPEAKER_OFF: 1397 // Nothing to do here 1398 return HANDLED; 1399 case SPEAKER_ON: 1400 // fall through 1401 case BT_AUDIO_CONNECTED: 1402 case SWITCH_BLUETOOTH: 1403 case USER_SWITCH_BLUETOOTH: 1404 case SWITCH_HEADSET: 1405 case USER_SWITCH_HEADSET: 1406 case SWITCH_SPEAKER: 1407 case USER_SWITCH_SPEAKER: 1408 return HANDLED; 1409 case SWITCH_FOCUS: 1410 if (msg.arg1 == NO_FOCUS) { 1411 reinitialize(); 1412 mCallAudioManager.notifyAudioOperationsComplete(); 1413 } 1414 return HANDLED; 1415 case STREAMING_FORCE_DISABLED: 1416 reinitialize(); 1417 return HANDLED; 1418 default: 1419 return NOT_HANDLED; 1420 } 1421 } 1422 } 1423 1424 private final BroadcastReceiver mMuteChangeReceiver = new BroadcastReceiver() { 1425 @Override 1426 public void onReceive(Context context, Intent intent) { 1427 Log.startSession("CARSM.mCR"); 1428 try { 1429 if (AudioManager.ACTION_MICROPHONE_MUTE_CHANGED.equals(intent.getAction())) { 1430 if (mCallsManager.isInEmergencyCall()) { 1431 Log.i(this, "Mute was externally changed when there's an emergency call. " + 1432 "Forcing mute back off."); 1433 sendInternalMessage(MUTE_OFF); 1434 } else { 1435 sendInternalMessage(MUTE_EXTERNALLY_CHANGED); 1436 } 1437 } else if (AudioManager.STREAM_MUTE_CHANGED_ACTION.equals(intent.getAction())) { 1438 int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1); 1439 boolean isStreamMuted = intent.getBooleanExtra( 1440 AudioManager.EXTRA_STREAM_VOLUME_MUTED, false); 1441 1442 if (streamType == AudioManager.STREAM_RING && !isStreamMuted) { 1443 Log.i(this, "Ring stream was un-muted."); 1444 mCallAudioManager.onRingerModeChange(); 1445 } 1446 } else { 1447 Log.w(this, "Received non-mute-change intent"); 1448 } 1449 } finally { 1450 Log.endSession(); 1451 } 1452 } 1453 }; 1454 1455 private final BroadcastReceiver mSpeakerPhoneChangeReceiver = new BroadcastReceiver() { 1456 @Override 1457 public void onReceive(Context context, Intent intent) { 1458 Log.startSession("CARSM.mSPCR"); 1459 try { 1460 if (AudioManager.ACTION_SPEAKERPHONE_STATE_CHANGED.equals(intent.getAction())) { 1461 if (mAudioManager != null) { 1462 if (mAudioManager.isSpeakerphoneOn()) { 1463 sendInternalMessage(SPEAKER_ON); 1464 } else { 1465 sendInternalMessage(SPEAKER_OFF); 1466 } 1467 } 1468 } else { 1469 Log.w(this, "Received non-speakerphone-change intent"); 1470 } 1471 } finally { 1472 Log.endSession(); 1473 } 1474 } 1475 }; 1476 1477 private final ActiveEarpieceRoute mActiveEarpieceRoute = new ActiveEarpieceRoute(); 1478 private final ActiveHeadsetRoute mActiveHeadsetRoute = new ActiveHeadsetRoute(); 1479 private final ActiveBluetoothRoute mActiveBluetoothRoute = new ActiveBluetoothRoute(); 1480 private final ActiveSpeakerRoute mActiveSpeakerRoute = new ActiveSpeakerRoute(); 1481 private final RingingBluetoothRoute mRingingBluetoothRoute = new RingingBluetoothRoute(); 1482 private final QuiescentEarpieceRoute mQuiescentEarpieceRoute = new QuiescentEarpieceRoute(); 1483 private final QuiescentHeadsetRoute mQuiescentHeadsetRoute = new QuiescentHeadsetRoute(); 1484 private final QuiescentBluetoothRoute mQuiescentBluetoothRoute = new QuiescentBluetoothRoute(); 1485 private final QuiescentSpeakerRoute mQuiescentSpeakerRoute = new QuiescentSpeakerRoute(); 1486 private final StreamingState mStreamingState = new StreamingState(); 1487 1488 private final Executor mAsyncTaskExecutor; 1489 1490 /** 1491 * A few pieces of hidden state. Used to avoid exponential explosion of number of explicit 1492 * states 1493 */ 1494 private int mDeviceSupportedRoutes; 1495 private int mAvailableRoutes; 1496 private int mAudioFocusType = NO_FOCUS; 1497 private boolean mWasOnSpeaker; 1498 private boolean mIsMuted; 1499 1500 private final Context mContext; 1501 private final CallsManager mCallsManager; 1502 private final AudioManager mAudioManager; 1503 private final BluetoothRouteManager mBluetoothRouteManager; 1504 private final WiredHeadsetManager mWiredHeadsetManager; 1505 private final StatusBarNotifier mStatusBarNotifier; 1506 private final CallAudioManager.AudioServiceFactory mAudioServiceFactory; 1507 private boolean mDoesDeviceSupportEarpieceRoute; 1508 private final TelecomSystem.SyncRoot mLock; 1509 private boolean mHasUserExplicitlyLeftBluetooth = false; 1510 1511 private HashMap<String, Integer> mStateNameToRouteCode; 1512 private HashMap<Integer, AudioState> mRouteCodeToQuiescentState; 1513 1514 // CallAudioState is used as an interface to communicate with many other system components. 1515 // No internal state transitions should depend on this variable. 1516 private CallAudioState mCurrentCallAudioState; 1517 private CallAudioState mLastKnownCallAudioState; 1518 1519 private CallAudioManager mCallAudioManager; 1520 private CallAudioCommunicationDeviceTracker mCommunicationDeviceTracker; 1521 private FeatureFlags mFeatureFlags; 1522 CallAudioRouteStateMachine( Context context, CallsManager callsManager, BluetoothRouteManager bluetoothManager, WiredHeadsetManager wiredHeadsetManager, StatusBarNotifier statusBarNotifier, CallAudioManager.AudioServiceFactory audioServiceFactory, int earpieceControl, Executor asyncTaskExecutor, CallAudioCommunicationDeviceTracker communicationDeviceTracker, FeatureFlags featureFlags)1523 public CallAudioRouteStateMachine( 1524 Context context, 1525 CallsManager callsManager, 1526 BluetoothRouteManager bluetoothManager, 1527 WiredHeadsetManager wiredHeadsetManager, 1528 StatusBarNotifier statusBarNotifier, 1529 CallAudioManager.AudioServiceFactory audioServiceFactory, 1530 int earpieceControl, 1531 Executor asyncTaskExecutor, 1532 CallAudioCommunicationDeviceTracker communicationDeviceTracker, 1533 FeatureFlags featureFlags) { 1534 super(NAME); 1535 mContext = context; 1536 mCallsManager = callsManager; 1537 mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); 1538 mBluetoothRouteManager = bluetoothManager; 1539 mWiredHeadsetManager = wiredHeadsetManager; 1540 mStatusBarNotifier = statusBarNotifier; 1541 mAudioServiceFactory = audioServiceFactory; 1542 mLock = callsManager.getLock(); 1543 mAsyncTaskExecutor = asyncTaskExecutor; 1544 mCommunicationDeviceTracker = communicationDeviceTracker; 1545 mFeatureFlags = featureFlags; 1546 createStates(earpieceControl); 1547 } 1548 1549 /** Used for testing only */ CallAudioRouteStateMachine( Context context, CallsManager callsManager, BluetoothRouteManager bluetoothManager, WiredHeadsetManager wiredHeadsetManager, StatusBarNotifier statusBarNotifier, CallAudioManager.AudioServiceFactory audioServiceFactory, int earpieceControl, Looper looper, Executor asyncTaskExecutor, CallAudioCommunicationDeviceTracker communicationDeviceTracker, FeatureFlags featureFlags)1550 public CallAudioRouteStateMachine( 1551 Context context, 1552 CallsManager callsManager, 1553 BluetoothRouteManager bluetoothManager, 1554 WiredHeadsetManager wiredHeadsetManager, 1555 StatusBarNotifier statusBarNotifier, 1556 CallAudioManager.AudioServiceFactory audioServiceFactory, 1557 int earpieceControl, Looper looper, Executor asyncTaskExecutor, 1558 CallAudioCommunicationDeviceTracker communicationDeviceTracker, 1559 FeatureFlags featureFlags) { 1560 super(NAME, looper); 1561 mContext = context; 1562 mCallsManager = callsManager; 1563 mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); 1564 mBluetoothRouteManager = bluetoothManager; 1565 mWiredHeadsetManager = wiredHeadsetManager; 1566 mStatusBarNotifier = statusBarNotifier; 1567 mAudioServiceFactory = audioServiceFactory; 1568 mLock = callsManager.getLock(); 1569 mAsyncTaskExecutor = asyncTaskExecutor; 1570 mCommunicationDeviceTracker = communicationDeviceTracker; 1571 mFeatureFlags = featureFlags; 1572 createStates(earpieceControl); 1573 } 1574 createStates(int earpieceControl)1575 private void createStates(int earpieceControl) { 1576 switch (earpieceControl) { 1577 case EARPIECE_FORCE_DISABLED: 1578 mDoesDeviceSupportEarpieceRoute = false; 1579 break; 1580 case EARPIECE_FORCE_ENABLED: 1581 mDoesDeviceSupportEarpieceRoute = true; 1582 break; 1583 default: 1584 mDoesDeviceSupportEarpieceRoute = checkForEarpieceSupport(); 1585 } 1586 1587 addState(mActiveEarpieceRoute); 1588 addState(mActiveHeadsetRoute); 1589 addState(mActiveBluetoothRoute); 1590 addState(mActiveSpeakerRoute); 1591 addState(mRingingBluetoothRoute); 1592 addState(mQuiescentEarpieceRoute); 1593 addState(mQuiescentHeadsetRoute); 1594 addState(mQuiescentBluetoothRoute); 1595 addState(mQuiescentSpeakerRoute); 1596 addState(mStreamingState); 1597 1598 1599 mStateNameToRouteCode = new HashMap<>(8); 1600 mStateNameToRouteCode.put(mQuiescentEarpieceRoute.getName(), ROUTE_EARPIECE); 1601 mStateNameToRouteCode.put(mQuiescentBluetoothRoute.getName(), ROUTE_BLUETOOTH); 1602 mStateNameToRouteCode.put(mQuiescentHeadsetRoute.getName(), ROUTE_WIRED_HEADSET); 1603 mStateNameToRouteCode.put(mQuiescentSpeakerRoute.getName(), ROUTE_SPEAKER); 1604 mStateNameToRouteCode.put(mRingingBluetoothRoute.getName(), ROUTE_BLUETOOTH); 1605 mStateNameToRouteCode.put(mActiveEarpieceRoute.getName(), ROUTE_EARPIECE); 1606 mStateNameToRouteCode.put(mActiveBluetoothRoute.getName(), ROUTE_BLUETOOTH); 1607 mStateNameToRouteCode.put(mActiveHeadsetRoute.getName(), ROUTE_WIRED_HEADSET); 1608 mStateNameToRouteCode.put(mActiveSpeakerRoute.getName(), ROUTE_SPEAKER); 1609 mStateNameToRouteCode.put(mStreamingState.getName(), ROUTE_STREAMING); 1610 1611 mRouteCodeToQuiescentState = new HashMap<>(4); 1612 mRouteCodeToQuiescentState.put(ROUTE_EARPIECE, mQuiescentEarpieceRoute); 1613 mRouteCodeToQuiescentState.put(ROUTE_BLUETOOTH, mQuiescentBluetoothRoute); 1614 mRouteCodeToQuiescentState.put(ROUTE_SPEAKER, mQuiescentSpeakerRoute); 1615 mRouteCodeToQuiescentState.put(ROUTE_WIRED_HEADSET, mQuiescentHeadsetRoute); 1616 mRouteCodeToQuiescentState.put(ROUTE_STREAMING, mStreamingState); 1617 } 1618 setCallAudioManager(CallAudioManager callAudioManager)1619 public void setCallAudioManager(CallAudioManager callAudioManager) { 1620 mCallAudioManager = callAudioManager; 1621 } 1622 1623 /** 1624 * Initializes the state machine with info on initial audio route, supported audio routes, 1625 * and mute status. 1626 */ initialize()1627 public void initialize() { 1628 CallAudioState initState = getInitialAudioState(); 1629 initialize(initState); 1630 } 1631 initialize(CallAudioState initState)1632 public void initialize(CallAudioState initState) { 1633 if ((initState.getRoute() & getCurrentCallSupportedRoutes()) == 0) { 1634 Log.e(this, new IllegalArgumentException(), "Route %d specified when supported call" + 1635 " routes are: %d", initState.getRoute(), getCurrentCallSupportedRoutes()); 1636 } 1637 1638 mCurrentCallAudioState = initState; 1639 mLastKnownCallAudioState = initState; 1640 mDeviceSupportedRoutes = initState.getSupportedRouteMask(); 1641 mAvailableRoutes = mDeviceSupportedRoutes & getCurrentCallSupportedRoutes(); 1642 mIsMuted = initState.isMuted(); 1643 mWasOnSpeaker = false; 1644 IntentFilter micMuteChangedFilter = new IntentFilter( 1645 AudioManager.ACTION_MICROPHONE_MUTE_CHANGED); 1646 micMuteChangedFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); 1647 mContext.registerReceiver(mMuteChangeReceiver, micMuteChangedFilter); 1648 1649 IntentFilter muteChangedFilter = new IntentFilter(AudioManager.STREAM_MUTE_CHANGED_ACTION); 1650 muteChangedFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); 1651 mContext.registerReceiver(mMuteChangeReceiver, muteChangedFilter); 1652 1653 IntentFilter speakerChangedFilter = new IntentFilter( 1654 AudioManager.ACTION_SPEAKERPHONE_STATE_CHANGED); 1655 speakerChangedFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); 1656 mContext.registerReceiver(mSpeakerPhoneChangeReceiver, speakerChangedFilter); 1657 1658 mStatusBarNotifier.notifyMute(initState.isMuted()); 1659 // We used to call mStatusBarNotifier.notifySpeakerphone, but that makes no sense as there 1660 // is never a call at this boot (init) time. 1661 setInitialState(mRouteCodeToQuiescentState.get(initState.getRoute())); 1662 start(); 1663 } 1664 1665 /** 1666 * Getter for the current CallAudioState object that the state machine is keeping track of. 1667 * Used for compatibility purposes. 1668 */ getCurrentCallAudioState()1669 public CallAudioState getCurrentCallAudioState() { 1670 return mCurrentCallAudioState; 1671 } 1672 sendMessageWithSessionInfo(int message, int arg)1673 public void sendMessageWithSessionInfo(int message, int arg) { 1674 sendMessageWithSessionInfo(message, arg, (String) null); 1675 } 1676 sendMessageWithSessionInfo(int message)1677 public void sendMessageWithSessionInfo(int message) { 1678 sendMessageWithSessionInfo(message, 0, (String) null); 1679 } 1680 sendMessageWithSessionInfo(int message, int arg, String data)1681 public void sendMessageWithSessionInfo(int message, int arg, String data) { 1682 SomeArgs args = SomeArgs.obtain(); 1683 args.arg1 = Log.createSubsession(); 1684 args.arg2 = data; 1685 sendMessage(message, arg, 0, args); 1686 } 1687 sendMessageWithSessionInfo(int message, int arg, int data)1688 public void sendMessageWithSessionInfo(int message, int arg, int data) { 1689 // ignore, only used in CallAudioRouteController 1690 } 1691 sendMessageWithSessionInfo(int message, int arg, BluetoothDevice bluetoothDevice)1692 public void sendMessageWithSessionInfo(int message, int arg, BluetoothDevice bluetoothDevice) { 1693 // ignore, only used in CallAudioRouteController 1694 } 1695 1696 @Override sendMessage(int message, Runnable r)1697 public void sendMessage(int message, Runnable r) { 1698 super.sendMessage(message, r); 1699 } 1700 1701 /** 1702 * This is for state-independent changes in audio route (i.e. muting or runnables) 1703 * @param msg that couldn't be handled. 1704 */ 1705 @Override unhandledMessage(Message msg)1706 protected void unhandledMessage(Message msg) { 1707 switch (msg.what) { 1708 case MUTE_ON: 1709 setMuteOn(true); 1710 updateSystemMuteState(); 1711 return; 1712 case MUTE_OFF: 1713 setMuteOn(false); 1714 updateSystemMuteState(); 1715 return; 1716 case MUTE_EXTERNALLY_CHANGED: 1717 mIsMuted = mAudioManager.isMicrophoneMute(); 1718 if (isInActiveState()) { 1719 updateSystemMuteState(); 1720 } 1721 return; 1722 case TOGGLE_MUTE: 1723 if (mIsMuted) { 1724 sendInternalMessage(MUTE_OFF); 1725 } else { 1726 sendInternalMessage(MUTE_ON); 1727 } 1728 return; 1729 case UPDATE_SYSTEM_AUDIO_ROUTE: 1730 if (mFeatureFlags.availableRoutesNeverUpdatedAfterSetSystemAudioState()) { 1731 // Ensure available routes is updated. 1732 updateRouteForForegroundCall(); 1733 // Ensure current audio state gets updated to take this into account. 1734 updateInternalCallAudioState(); 1735 // Either resend the current audio state as it stands, or update to reflect any 1736 // changes put into place based on mAvailableRoutes 1737 setSystemAudioState(mCurrentCallAudioState, true); 1738 } else { 1739 updateInternalCallAudioState(); 1740 updateRouteForForegroundCall(); 1741 resendSystemAudioState(); 1742 } 1743 return; 1744 case RUN_RUNNABLE: 1745 java.lang.Runnable r = (java.lang.Runnable) msg.obj; 1746 r.run(); 1747 return; 1748 default: 1749 Log.e(this, new IllegalStateException(), "Unexpected message code %d", msg.what); 1750 } 1751 } 1752 quitStateMachine()1753 public void quitStateMachine() { 1754 quitNow(); 1755 } 1756 dump(IndentingPrintWriter pw)1757 public void dump(IndentingPrintWriter pw) { 1758 pw.print("Current state: "); 1759 pw.println(getCurrentState().getName()); 1760 pw.println("Pending messages:"); 1761 pw.increaseIndent(); 1762 dumpPendingMessages(pw); 1763 pw.decreaseIndent(); 1764 } 1765 dumpPendingMessages(IndentingPrintWriter pw)1766 public void dumpPendingMessages(IndentingPrintWriter pw) { 1767 getAdapterHandler().getLooper().dump(pw::println, ""); 1768 } 1769 isHfpDeviceAvailable()1770 public boolean isHfpDeviceAvailable() { 1771 return mBluetoothRouteManager.isBluetoothAvailable(); 1772 } 1773 setSpeakerphoneOn(boolean on)1774 private void setSpeakerphoneOn(boolean on) { 1775 Log.i(this, "turning speaker phone %s", on); 1776 final boolean hasAnyCalls = mCallsManager.hasAnyCalls(); 1777 // These APIs are all via two-way binder calls so can potentially block Telecom. Since none 1778 // of this has to happen in the Telecom lock we'll offload it to the async executor. 1779 boolean speakerOn = false; 1780 if (mFeatureFlags.callAudioCommunicationDeviceRefactor()) { 1781 if (on) { 1782 speakerOn = mCommunicationDeviceTracker.setCommunicationDevice( 1783 AudioDeviceInfo.TYPE_BUILTIN_SPEAKER, null); 1784 } else { 1785 mCommunicationDeviceTracker.clearCommunicationDevice( 1786 AudioDeviceInfo.TYPE_BUILTIN_SPEAKER); 1787 } 1788 } else { 1789 speakerOn = processLegacySpeakerCommunicationDevice(on); 1790 } 1791 mStatusBarNotifier.notifySpeakerphone(hasAnyCalls && speakerOn); 1792 } 1793 setBluetoothOn(String address)1794 private void setBluetoothOn(String address) { 1795 if (mBluetoothRouteManager.isBluetoothAvailable()) { 1796 BluetoothDevice connectedDevice = 1797 mBluetoothRouteManager.getBluetoothAudioConnectedDevice(); 1798 if (address == null && connectedDevice != null) { 1799 // null means connect to any device, so if we're already connected to some device, 1800 // that means we can just tell ourselves that it's connected. 1801 // Do still try to connect audio though, so that BluetoothRouteManager knows that 1802 // there's an active call. 1803 Log.i(this, "Bluetooth audio already on."); 1804 sendInternalMessage(BT_AUDIO_CONNECTED); 1805 mBluetoothRouteManager.connectBluetoothAudio(connectedDevice.getAddress()); 1806 return; 1807 } 1808 if (connectedDevice == null || !Objects.equals(address, connectedDevice.getAddress())) { 1809 Log.i(this, "connecting bluetooth audio: %s", address); 1810 mBluetoothRouteManager.connectBluetoothAudio(address); 1811 } 1812 } 1813 } 1814 setBluetoothOff()1815 private void setBluetoothOff() { 1816 if (mBluetoothRouteManager.isBluetoothAvailable()) { 1817 if (mBluetoothRouteManager.isBluetoothAudioConnectedOrPending()) { 1818 Log.i(this, "disconnecting bluetooth audio"); 1819 mBluetoothRouteManager.disconnectBluetoothAudio(); 1820 } 1821 } 1822 } 1823 setMuteOn(boolean mute)1824 private void setMuteOn(boolean mute) { 1825 mIsMuted = mute; 1826 Log.addEvent(mCallsManager.getForegroundCall(), mute ? 1827 LogUtils.Events.MUTE : LogUtils.Events.UNMUTE); 1828 if (mute != mAudioManager.isMicrophoneMute() && isInActiveState()) { 1829 IAudioService audio = mAudioServiceFactory.getAudioService(); 1830 Log.i(this, "changing microphone mute state to: %b [serviceIsNull=%b]", 1831 mute, audio == null); 1832 if (audio != null) { 1833 try { 1834 // We use the audio service directly here so that we can specify 1835 // the current user. Telecom runs in the system_server process which 1836 // may run as a separate user from the foreground user. If we 1837 // used AudioManager directly, we would change mute for the system's 1838 // user and not the current foreground, which we want to avoid. 1839 audio.setMicrophoneMute(mute, mContext.getOpPackageName(), 1840 getCurrentUserId(), mContext.getAttributionTag()); 1841 } catch (RemoteException e) { 1842 Log.e(this, e, "Remote exception while toggling mute."); 1843 } 1844 // TODO: Check microphone state after attempting to set to ensure that 1845 // our state corroborates AudioManager's state. 1846 } 1847 } 1848 } 1849 updateSystemMuteState()1850 private void updateSystemMuteState() { 1851 CallAudioState newCallAudioState = new CallAudioState(mIsMuted, 1852 mCurrentCallAudioState.getRoute(), 1853 mAvailableRoutes, 1854 mCurrentCallAudioState.getActiveBluetoothDevice(), 1855 mBluetoothRouteManager.getConnectedDevices()); 1856 setSystemAudioState(newCallAudioState); 1857 updateInternalCallAudioState(); 1858 } 1859 1860 /** 1861 * Updates the CallAudioState object from current internal state. The result is used for 1862 * external communication only. 1863 */ updateInternalCallAudioState()1864 private void updateInternalCallAudioState() { 1865 IState currentState = getCurrentState(); 1866 if (currentState == null) { 1867 Log.e(this, new IllegalStateException(), "Current state should never be null" + 1868 " when updateInternalCallAudioState is called."); 1869 mCurrentCallAudioState = new CallAudioState( 1870 mIsMuted, mCurrentCallAudioState.getRoute(), mAvailableRoutes, 1871 mBluetoothRouteManager.getBluetoothAudioConnectedDevice(), 1872 mBluetoothRouteManager.getConnectedDevices()); 1873 return; 1874 } 1875 int currentRoute = mStateNameToRouteCode.get(currentState.getName()); 1876 mCurrentCallAudioState = new CallAudioState(mIsMuted, currentRoute, mAvailableRoutes, 1877 mBluetoothRouteManager.getBluetoothAudioConnectedDevice(), 1878 mBluetoothRouteManager.getConnectedDevices()); 1879 } 1880 setSystemAudioState(CallAudioState newCallAudioState)1881 private void setSystemAudioState(CallAudioState newCallAudioState) { 1882 setSystemAudioState(newCallAudioState, false); 1883 } 1884 resendSystemAudioState()1885 private void resendSystemAudioState() { 1886 setSystemAudioState(mLastKnownCallAudioState, true); 1887 } 1888 1889 @VisibleForTesting getLastKnownCallAudioState()1890 public CallAudioState getLastKnownCallAudioState() { 1891 return mLastKnownCallAudioState; 1892 } 1893 setSystemAudioState(CallAudioState newCallAudioState, boolean force)1894 private void setSystemAudioState(CallAudioState newCallAudioState, boolean force) { 1895 synchronized (mLock) { 1896 Log.i(this, "setSystemAudioState: changing from %s to %s", mLastKnownCallAudioState, 1897 newCallAudioState); 1898 if (force || !newCallAudioState.equals(mLastKnownCallAudioState)) { 1899 mStatusBarNotifier.notifyMute(newCallAudioState.isMuted()); 1900 mCallsManager.onCallAudioStateChanged(mLastKnownCallAudioState, newCallAudioState); 1901 updateAudioStateForTrackedCalls(newCallAudioState); 1902 mLastKnownCallAudioState = newCallAudioState; 1903 } 1904 } 1905 } 1906 updateAudioStateForTrackedCalls(CallAudioState newCallAudioState)1907 private void updateAudioStateForTrackedCalls(CallAudioState newCallAudioState) { 1908 Set<Call> calls = mCallsManager.getTrackedCalls(); 1909 for (Call call : calls) { 1910 if (call != null && call.getConnectionService() != null) { 1911 call.getConnectionService().onCallAudioStateChanged(call, newCallAudioState); 1912 } 1913 } 1914 } 1915 calculateSupportedRoutes()1916 private int calculateSupportedRoutes() { 1917 int routeMask = CallAudioState.ROUTE_SPEAKER; 1918 1919 if (mWiredHeadsetManager.isPluggedIn()) { 1920 routeMask |= CallAudioState.ROUTE_WIRED_HEADSET; 1921 } else if (mDoesDeviceSupportEarpieceRoute){ 1922 routeMask |= CallAudioState.ROUTE_EARPIECE; 1923 } 1924 1925 if (mBluetoothRouteManager.isBluetoothAvailable()) { 1926 routeMask |= CallAudioState.ROUTE_BLUETOOTH; 1927 } 1928 1929 return routeMask; 1930 } 1931 sendInternalMessage(int messageCode)1932 private void sendInternalMessage(int messageCode) { 1933 sendInternalMessage(messageCode, 0); 1934 } 1935 sendInternalMessage(int messageCode, int arg1)1936 private void sendInternalMessage(int messageCode, int arg1) { 1937 // Internal messages are messages which the state machine sends to itself in the 1938 // course of processing externally-sourced messages. We want to send these messages at 1939 // the front of the queue in order to make actions appear atomic to the user and to 1940 // prevent scenarios such as these: 1941 // 1. State machine handler thread is suspended for some reason. 1942 // 2. Headset gets connected (sends CONNECT_HEADSET). 1943 // 3. User switches to speakerphone in the UI (sends SWITCH_SPEAKER). 1944 // 4. State machine handler is un-suspended. 1945 // 5. State machine handler processes the CONNECT_HEADSET message and sends 1946 // SWITCH_HEADSET at end of queue. 1947 // 6. State machine handler processes SWITCH_SPEAKER. 1948 // 7. State machine handler processes SWITCH_HEADSET. 1949 Session subsession = Log.createSubsession(); 1950 if(subsession != null) { 1951 SomeArgs args = SomeArgs.obtain(); 1952 args.arg1 = subsession; 1953 sendMessageAtFrontOfQueue(messageCode, arg1, 0, args); 1954 } else { 1955 sendMessageAtFrontOfQueue(messageCode, arg1); 1956 } 1957 } 1958 getInitialAudioState()1959 private CallAudioState getInitialAudioState() { 1960 int supportedRouteMask = calculateSupportedRoutes() & getCurrentCallSupportedRoutes(); 1961 final int route; 1962 1963 if ((supportedRouteMask & ROUTE_BLUETOOTH) != 0 1964 && mBluetoothRouteManager.hasBtActiveDevice()) { 1965 route = ROUTE_BLUETOOTH; 1966 } else if ((supportedRouteMask & ROUTE_WIRED_HEADSET) != 0) { 1967 route = ROUTE_WIRED_HEADSET; 1968 } else if ((supportedRouteMask & ROUTE_EARPIECE) != 0) { 1969 route = ROUTE_EARPIECE; 1970 } else { 1971 route = ROUTE_SPEAKER; 1972 } 1973 1974 return new CallAudioState(false, route, supportedRouteMask, null, 1975 mBluetoothRouteManager.getConnectedDevices()); 1976 } 1977 getCurrentUserId()1978 private int getCurrentUserId() { 1979 final long ident = Binder.clearCallingIdentity(); 1980 try { 1981 UserInfo currentUser = ActivityManager.getService().getCurrentUser(); 1982 return currentUser.id; 1983 } catch (RemoteException e) { 1984 // Activity manager not running, nothing we can do assume user 0. 1985 } finally { 1986 Binder.restoreCallingIdentity(ident); 1987 } 1988 return UserHandle.USER_OWNER; 1989 } 1990 isInActiveState()1991 public boolean isInActiveState() { 1992 AudioState currentState = (AudioState) getCurrentState(); 1993 if (currentState == null) { 1994 Log.w(this, "Current state is null, assuming inactive state"); 1995 return false; 1996 } 1997 return currentState.isActive(); 1998 } 1999 checkForEarpieceSupport()2000 private boolean checkForEarpieceSupport() { 2001 AudioDeviceInfo[] deviceList = mAudioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS); 2002 for (AudioDeviceInfo device: deviceList) { 2003 if (device.getType() == AudioDeviceInfo.TYPE_BUILTIN_EARPIECE) { 2004 return true; 2005 } 2006 } 2007 // No earpiece found 2008 return false; 2009 } 2010 isWatchActiveOrOnlyWatchesAvailable()2011 private boolean isWatchActiveOrOnlyWatchesAvailable() { 2012 if (!mFeatureFlags.ignoreAutoRouteToWatchDevice()) { 2013 Log.i(this, "isWatchActiveOrOnlyWatchesAvailable: Flag is disabled."); 2014 return false; 2015 } 2016 2017 boolean containsWatchDevice = false; 2018 boolean containsNonWatchDevice = false; 2019 Collection<BluetoothDevice> connectedBtDevices = 2020 mBluetoothRouteManager.getConnectedDevices(); 2021 2022 for (BluetoothDevice connectedDevice: connectedBtDevices) { 2023 if (mBluetoothRouteManager.isWatch(connectedDevice)) { 2024 containsWatchDevice = true; 2025 } else { 2026 containsNonWatchDevice = true; 2027 } 2028 } 2029 2030 // Don't ignore switch if watch is already the active device. 2031 boolean isActiveDeviceWatch = mBluetoothRouteManager.isWatch( 2032 mBluetoothRouteManager.getBluetoothAudioConnectedDevice()); 2033 Log.i(this, "isWatchActiveOrOnlyWatchesAvailable: contains watch: %s, contains " 2034 + "non-wearable device: %s, is active device a watch: %s.", 2035 containsWatchDevice, containsNonWatchDevice, isActiveDeviceWatch); 2036 return containsWatchDevice && !containsNonWatchDevice && !isActiveDeviceWatch; 2037 } 2038 processLegacySpeakerCommunicationDevice(boolean on)2039 private boolean processLegacySpeakerCommunicationDevice(boolean on) { 2040 AudioDeviceInfo speakerDevice = null; 2041 for (AudioDeviceInfo info : mAudioManager.getAvailableCommunicationDevices()) { 2042 if (info.getType() == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER) { 2043 speakerDevice = info; 2044 break; 2045 } 2046 } 2047 boolean speakerOn = false; 2048 if (speakerDevice != null && on) { 2049 boolean result = mAudioManager.setCommunicationDevice(speakerDevice); 2050 if (result) { 2051 speakerOn = true; 2052 } 2053 } else { 2054 AudioDeviceInfo curDevice = mAudioManager.getCommunicationDevice(); 2055 if (curDevice != null 2056 && curDevice.getType() == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER) { 2057 mAudioManager.clearCommunicationDevice(); 2058 } 2059 } 2060 return speakerOn; 2061 } 2062 calculateBaselineRouteMessage(boolean isExplicitUserRequest, boolean includeBluetooth)2063 private int calculateBaselineRouteMessage(boolean isExplicitUserRequest, 2064 boolean includeBluetooth) { 2065 boolean isSkipEarpiece = false; 2066 if (!isExplicitUserRequest) { 2067 synchronized (mLock) { 2068 // Check video calls to skip earpiece since the baseline for video 2069 // calls should be the speakerphone route 2070 isSkipEarpiece = mCallsManager.hasVideoCall(); 2071 } 2072 } 2073 if ((mAvailableRoutes & ROUTE_BLUETOOTH) != 0 2074 && !mHasUserExplicitlyLeftBluetooth 2075 && includeBluetooth && !isWatchActiveOrOnlyWatchesAvailable()) { 2076 return isExplicitUserRequest ? USER_SWITCH_BLUETOOTH : SWITCH_BLUETOOTH; 2077 } else if ((mAvailableRoutes & ROUTE_EARPIECE) != 0 && !isSkipEarpiece) { 2078 return isExplicitUserRequest ? USER_SWITCH_EARPIECE : SWITCH_EARPIECE; 2079 } else if ((mAvailableRoutes & ROUTE_WIRED_HEADSET) != 0) { 2080 return isExplicitUserRequest ? USER_SWITCH_HEADSET : SWITCH_HEADSET; 2081 } else { 2082 return isExplicitUserRequest ? USER_SWITCH_SPEAKER : SWITCH_SPEAKER; 2083 } 2084 } 2085 reinitialize()2086 private void reinitialize() { 2087 CallAudioState initState = getInitialAudioState(); 2088 mDeviceSupportedRoutes = initState.getSupportedRouteMask(); 2089 mAvailableRoutes = mDeviceSupportedRoutes & getCurrentCallSupportedRoutes(); 2090 mIsMuted = initState.isMuted(); 2091 setSpeakerphoneOn(initState.getRoute() == CallAudioState.ROUTE_SPEAKER); 2092 setMuteOn(mIsMuted); 2093 mWasOnSpeaker = false; 2094 mHasUserExplicitlyLeftBluetooth = false; 2095 mLastKnownCallAudioState = initState; 2096 transitionTo(mRouteCodeToQuiescentState.get(initState.getRoute())); 2097 } 2098 updateRouteForForegroundCall()2099 private void updateRouteForForegroundCall() { 2100 mAvailableRoutes = mDeviceSupportedRoutes & getCurrentCallSupportedRoutes(); 2101 2102 CallAudioState currentState = getCurrentCallAudioState(); 2103 2104 // Move to baseline route in the case the current route is no longer available. 2105 if ((mAvailableRoutes & currentState.getRoute()) == 0) { 2106 sendInternalMessage(calculateBaselineRouteMessage(false, true)); 2107 } 2108 } 2109 getCurrentCallSupportedRoutes()2110 private int getCurrentCallSupportedRoutes() { 2111 int supportedRoutes = CallAudioState.ROUTE_ALL; 2112 2113 Call foregroundCall = mCallsManager.getForegroundCall(); 2114 if (foregroundCall != null) { 2115 supportedRoutes &= foregroundCall.getSupportedAudioRoutes(); 2116 } 2117 2118 return supportedRoutes; 2119 } 2120 modifyRoutes(int base, int remove, int add, boolean considerCurrentCall)2121 private int modifyRoutes(int base, int remove, int add, boolean considerCurrentCall) { 2122 base &= ~remove; 2123 2124 if (considerCurrentCall) { 2125 add &= getCurrentCallSupportedRoutes(); 2126 } 2127 2128 base |= add; 2129 2130 return base; 2131 } 2132 2133 @Override getAdapterHandler()2134 public Handler getAdapterHandler() { 2135 return getHandler(); 2136 } 2137 2138 @Override getPendingAudioRoute()2139 public PendingAudioRoute getPendingAudioRoute() { 2140 // Only used by CallAudioRouteController. 2141 return null; 2142 } 2143 } 2144