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