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