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