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