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