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