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 setSpeakerphoneOn(true); 417 // fall through 418 case SPEAKER_ON: 419 transitionTo(mActiveSpeakerRoute); 420 return HANDLED; 421 case SWITCH_FOCUS: 422 if (msg.arg1 == NO_FOCUS) { 423 reinitialize(); 424 mCallAudioManager.notifyAudioOperationsComplete(); 425 } 426 return HANDLED; 427 default: 428 return NOT_HANDLED; 429 } 430 } 431 } 432 433 class QuiescentEarpieceRoute extends EarpieceRoute { 434 @Override getName()435 public String getName() { 436 return QUIESCENT_EARPIECE_ROUTE_NAME; 437 } 438 439 @Override isActive()440 public boolean isActive() { 441 return false; 442 } 443 444 @Override enter()445 public void enter() { 446 super.enter(); 447 mHasUserExplicitlyLeftBluetooth = false; 448 updateInternalCallAudioState(); 449 } 450 451 @Override updateSystemAudioState()452 public void updateSystemAudioState() { 453 updateInternalCallAudioState(); 454 } 455 456 @Override processMessage(Message msg)457 public boolean processMessage(Message msg) { 458 if (super.processMessage(msg) == HANDLED) { 459 return HANDLED; 460 } 461 switch (msg.what) { 462 case SWITCH_EARPIECE: 463 case USER_SWITCH_EARPIECE: 464 case SPEAKER_ON: 465 // Ignore speakerphone state changes outside of calls. 466 case SPEAKER_OFF: 467 // Nothing to do here 468 return HANDLED; 469 case BT_AUDIO_CONNECTED: 470 Log.w(this, "BT Audio came on in quiescent earpiece route."); 471 transitionTo(mActiveBluetoothRoute); 472 return HANDLED; 473 case SWITCH_BLUETOOTH: 474 case USER_SWITCH_BLUETOOTH: 475 if ((mAvailableRoutes & ROUTE_BLUETOOTH) != 0) { 476 transitionTo(mQuiescentBluetoothRoute); 477 } else { 478 Log.w(this, "Ignoring switch to bluetooth command. Not available."); 479 } 480 return HANDLED; 481 case SWITCH_HEADSET: 482 case USER_SWITCH_HEADSET: 483 if ((mAvailableRoutes & ROUTE_WIRED_HEADSET) != 0) { 484 transitionTo(mQuiescentHeadsetRoute); 485 } else { 486 Log.w(this, "Ignoring switch to headset command. Not available."); 487 } 488 return HANDLED; 489 case SWITCH_SPEAKER: 490 case USER_SWITCH_SPEAKER: 491 transitionTo(mQuiescentSpeakerRoute); 492 return HANDLED; 493 case SWITCH_FOCUS: 494 if (msg.arg1 == ACTIVE_FOCUS || msg.arg1 == RINGING_FOCUS) { 495 transitionTo(mActiveEarpieceRoute); 496 } else { 497 mCallAudioManager.notifyAudioOperationsComplete(); 498 } 499 return HANDLED; 500 default: 501 return NOT_HANDLED; 502 } 503 } 504 } 505 506 abstract class EarpieceRoute extends AudioState { 507 @Override getRouteCode()508 public int getRouteCode() { 509 return CallAudioState.ROUTE_EARPIECE; 510 } 511 512 @Override processMessage(Message msg)513 public boolean processMessage(Message msg) { 514 if (super.processMessage(msg) == HANDLED) { 515 return HANDLED; 516 } 517 switch (msg.what) { 518 case CONNECT_WIRED_HEADSET: 519 sendInternalMessage(SWITCH_HEADSET); 520 return HANDLED; 521 case BT_ACTIVE_DEVICE_PRESENT: 522 if (!mHasUserExplicitlyLeftBluetooth) { 523 sendInternalMessage(SWITCH_BLUETOOTH); 524 } else { 525 Log.i(this, "Not switching to BT route from earpiece because user has " + 526 "explicitly disconnected."); 527 } 528 return HANDLED; 529 case BT_ACTIVE_DEVICE_GONE: 530 // No change in audio route required 531 return HANDLED; 532 case DISCONNECT_WIRED_HEADSET: 533 Log.e(this, new IllegalStateException(), 534 "Wired headset should not go from connected to not when on " + 535 "earpiece"); 536 return HANDLED; 537 case BT_AUDIO_DISCONNECTED: 538 // This may be sent as a confirmation by the BT stack after switch off BT. 539 return HANDLED; 540 case CONNECT_DOCK: 541 setSpeakerphoneOn(true); 542 sendInternalMessage(SWITCH_SPEAKER); 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 SCO audio here instead of routing away from BT entirely. 879 mBluetoothRouteManager.disconnectSco(); 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 SPEAKER_OFF: 1277 sendInternalMessage(SWITCH_BASELINE_ROUTE, INCLUDE_BLUETOOTH_IN_BASELINE); 1278 return HANDLED; 1279 case SWITCH_FOCUS: 1280 if (msg.arg1 == ACTIVE_FOCUS || msg.arg1 == RINGING_FOCUS) { 1281 setSpeakerphoneOn(true); 1282 transitionTo(mActiveSpeakerRoute); 1283 } else { 1284 mCallAudioManager.notifyAudioOperationsComplete(); 1285 } 1286 return HANDLED; 1287 default: 1288 return NOT_HANDLED; 1289 } 1290 } 1291 } 1292 1293 abstract class SpeakerRoute extends AudioState { 1294 @Override getRouteCode()1295 public int getRouteCode() { 1296 return CallAudioState.ROUTE_SPEAKER; 1297 } 1298 1299 @Override processMessage(Message msg)1300 public boolean processMessage(Message msg) { 1301 if (super.processMessage(msg) == HANDLED) { 1302 return HANDLED; 1303 } 1304 switch (msg.what) { 1305 case CONNECT_WIRED_HEADSET: 1306 sendInternalMessage(SWITCH_HEADSET); 1307 return HANDLED; 1308 case BT_ACTIVE_DEVICE_PRESENT: 1309 if (!mHasUserExplicitlyLeftBluetooth) { 1310 sendInternalMessage(SWITCH_BLUETOOTH); 1311 } else { 1312 Log.i(this, "Not switching to BT route from speaker because user has " + 1313 "explicitly disconnected."); 1314 } 1315 return HANDLED; 1316 case BT_ACTIVE_DEVICE_GONE: 1317 // No change in audio route required 1318 return HANDLED; 1319 case DISCONNECT_WIRED_HEADSET: 1320 // No change in audio route required 1321 return HANDLED; 1322 case BT_AUDIO_DISCONNECTED: 1323 // This may be sent as a confirmation by the BT stack after switch off BT. 1324 return HANDLED; 1325 case CONNECT_DOCK: 1326 // Nothing to do here 1327 return HANDLED; 1328 case DISCONNECT_DOCK: 1329 sendInternalMessage(SWITCH_BASELINE_ROUTE, INCLUDE_BLUETOOTH_IN_BASELINE); 1330 return HANDLED; 1331 default: 1332 return NOT_HANDLED; 1333 } 1334 } 1335 } 1336 1337 private final BroadcastReceiver mMuteChangeReceiver = new BroadcastReceiver() { 1338 @Override 1339 public void onReceive(Context context, Intent intent) { 1340 Log.startSession("CARSM.mCR"); 1341 try { 1342 if (AudioManager.ACTION_MICROPHONE_MUTE_CHANGED.equals(intent.getAction())) { 1343 if (mCallsManager.isInEmergencyCall()) { 1344 Log.i(this, "Mute was externally changed when there's an emergency call. " + 1345 "Forcing mute back off."); 1346 sendInternalMessage(MUTE_OFF); 1347 } else { 1348 sendInternalMessage(MUTE_EXTERNALLY_CHANGED); 1349 } 1350 } else if (AudioManager.STREAM_MUTE_CHANGED_ACTION.equals(intent.getAction())) { 1351 int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1); 1352 boolean isStreamMuted = intent.getBooleanExtra( 1353 AudioManager.EXTRA_STREAM_VOLUME_MUTED, false); 1354 1355 if (streamType == AudioManager.STREAM_RING && !isStreamMuted) { 1356 Log.i(this, "Ring stream was un-muted."); 1357 mCallAudioManager.onRingerModeChange(); 1358 } 1359 } else { 1360 Log.w(this, "Received non-mute-change intent"); 1361 } 1362 } finally { 1363 Log.endSession(); 1364 } 1365 } 1366 }; 1367 1368 private final BroadcastReceiver mSpeakerPhoneChangeReceiver = new BroadcastReceiver() { 1369 @Override 1370 public void onReceive(Context context, Intent intent) { 1371 Log.startSession("CARSM.mSPCR"); 1372 try { 1373 if (AudioManager.ACTION_SPEAKERPHONE_STATE_CHANGED.equals(intent.getAction())) { 1374 if (mAudioManager != null) { 1375 if (mAudioManager.isSpeakerphoneOn()) { 1376 sendInternalMessage(SPEAKER_ON); 1377 } else { 1378 sendInternalMessage(SPEAKER_OFF); 1379 } 1380 } 1381 } else { 1382 Log.w(this, "Received non-speakerphone-change intent"); 1383 } 1384 } finally { 1385 Log.endSession(); 1386 } 1387 } 1388 }; 1389 1390 private final ActiveEarpieceRoute mActiveEarpieceRoute = new ActiveEarpieceRoute(); 1391 private final ActiveHeadsetRoute mActiveHeadsetRoute = new ActiveHeadsetRoute(); 1392 private final ActiveBluetoothRoute mActiveBluetoothRoute = new ActiveBluetoothRoute(); 1393 private final ActiveSpeakerRoute mActiveSpeakerRoute = new ActiveSpeakerRoute(); 1394 private final RingingBluetoothRoute mRingingBluetoothRoute = new RingingBluetoothRoute(); 1395 private final QuiescentEarpieceRoute mQuiescentEarpieceRoute = new QuiescentEarpieceRoute(); 1396 private final QuiescentHeadsetRoute mQuiescentHeadsetRoute = new QuiescentHeadsetRoute(); 1397 private final QuiescentBluetoothRoute mQuiescentBluetoothRoute = new QuiescentBluetoothRoute(); 1398 private final QuiescentSpeakerRoute mQuiescentSpeakerRoute = new QuiescentSpeakerRoute(); 1399 1400 /** 1401 * A few pieces of hidden state. Used to avoid exponential explosion of number of explicit 1402 * states 1403 */ 1404 private int mDeviceSupportedRoutes; 1405 private int mAvailableRoutes; 1406 private int mAudioFocusType = NO_FOCUS; 1407 private boolean mWasOnSpeaker; 1408 private boolean mIsMuted; 1409 1410 private final Context mContext; 1411 private final CallsManager mCallsManager; 1412 private final AudioManager mAudioManager; 1413 private final BluetoothRouteManager mBluetoothRouteManager; 1414 private final WiredHeadsetManager mWiredHeadsetManager; 1415 private final StatusBarNotifier mStatusBarNotifier; 1416 private final CallAudioManager.AudioServiceFactory mAudioServiceFactory; 1417 private boolean mDoesDeviceSupportEarpieceRoute; 1418 private final TelecomSystem.SyncRoot mLock; 1419 private boolean mHasUserExplicitlyLeftBluetooth = false; 1420 1421 private HashMap<String, Integer> mStateNameToRouteCode; 1422 private HashMap<Integer, AudioState> mRouteCodeToQuiescentState; 1423 1424 // CallAudioState is used as an interface to communicate with many other system components. 1425 // No internal state transitions should depend on this variable. 1426 private CallAudioState mCurrentCallAudioState; 1427 private CallAudioState mLastKnownCallAudioState; 1428 1429 private CallAudioManager mCallAudioManager; 1430 CallAudioRouteStateMachine( Context context, CallsManager callsManager, BluetoothRouteManager bluetoothManager, WiredHeadsetManager wiredHeadsetManager, StatusBarNotifier statusBarNotifier, CallAudioManager.AudioServiceFactory audioServiceFactory, int earpieceControl)1431 public CallAudioRouteStateMachine( 1432 Context context, 1433 CallsManager callsManager, 1434 BluetoothRouteManager bluetoothManager, 1435 WiredHeadsetManager wiredHeadsetManager, 1436 StatusBarNotifier statusBarNotifier, 1437 CallAudioManager.AudioServiceFactory audioServiceFactory, 1438 int earpieceControl) { 1439 super(NAME); 1440 mContext = context; 1441 mCallsManager = callsManager; 1442 mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); 1443 mBluetoothRouteManager = bluetoothManager; 1444 mWiredHeadsetManager = wiredHeadsetManager; 1445 mStatusBarNotifier = statusBarNotifier; 1446 mAudioServiceFactory = audioServiceFactory; 1447 mLock = callsManager.getLock(); 1448 1449 createStates(earpieceControl); 1450 } 1451 1452 /** Used for testing only */ CallAudioRouteStateMachine( Context context, CallsManager callsManager, BluetoothRouteManager bluetoothManager, WiredHeadsetManager wiredHeadsetManager, StatusBarNotifier statusBarNotifier, CallAudioManager.AudioServiceFactory audioServiceFactory, int earpieceControl, Looper looper)1453 public CallAudioRouteStateMachine( 1454 Context context, 1455 CallsManager callsManager, 1456 BluetoothRouteManager bluetoothManager, 1457 WiredHeadsetManager wiredHeadsetManager, 1458 StatusBarNotifier statusBarNotifier, 1459 CallAudioManager.AudioServiceFactory audioServiceFactory, 1460 int earpieceControl, Looper looper) { 1461 super(NAME, looper); 1462 mContext = context; 1463 mCallsManager = callsManager; 1464 mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); 1465 mBluetoothRouteManager = bluetoothManager; 1466 mWiredHeadsetManager = wiredHeadsetManager; 1467 mStatusBarNotifier = statusBarNotifier; 1468 mAudioServiceFactory = audioServiceFactory; 1469 mLock = callsManager.getLock(); 1470 1471 createStates(earpieceControl); 1472 } 1473 createStates(int earpieceControl)1474 private void createStates(int earpieceControl) { 1475 switch (earpieceControl) { 1476 case EARPIECE_FORCE_DISABLED: 1477 mDoesDeviceSupportEarpieceRoute = false; 1478 break; 1479 case EARPIECE_FORCE_ENABLED: 1480 mDoesDeviceSupportEarpieceRoute = true; 1481 break; 1482 default: 1483 mDoesDeviceSupportEarpieceRoute = checkForEarpieceSupport(); 1484 } 1485 1486 addState(mActiveEarpieceRoute); 1487 addState(mActiveHeadsetRoute); 1488 addState(mActiveBluetoothRoute); 1489 addState(mActiveSpeakerRoute); 1490 addState(mRingingBluetoothRoute); 1491 addState(mQuiescentEarpieceRoute); 1492 addState(mQuiescentHeadsetRoute); 1493 addState(mQuiescentBluetoothRoute); 1494 addState(mQuiescentSpeakerRoute); 1495 1496 1497 mStateNameToRouteCode = new HashMap<>(8); 1498 mStateNameToRouteCode.put(mQuiescentEarpieceRoute.getName(), ROUTE_EARPIECE); 1499 mStateNameToRouteCode.put(mQuiescentBluetoothRoute.getName(), ROUTE_BLUETOOTH); 1500 mStateNameToRouteCode.put(mQuiescentHeadsetRoute.getName(), ROUTE_WIRED_HEADSET); 1501 mStateNameToRouteCode.put(mQuiescentSpeakerRoute.getName(), ROUTE_SPEAKER); 1502 mStateNameToRouteCode.put(mRingingBluetoothRoute.getName(), ROUTE_BLUETOOTH); 1503 mStateNameToRouteCode.put(mActiveEarpieceRoute.getName(), ROUTE_EARPIECE); 1504 mStateNameToRouteCode.put(mActiveBluetoothRoute.getName(), ROUTE_BLUETOOTH); 1505 mStateNameToRouteCode.put(mActiveHeadsetRoute.getName(), ROUTE_WIRED_HEADSET); 1506 mStateNameToRouteCode.put(mActiveSpeakerRoute.getName(), ROUTE_SPEAKER); 1507 1508 mRouteCodeToQuiescentState = new HashMap<>(4); 1509 mRouteCodeToQuiescentState.put(ROUTE_EARPIECE, mQuiescentEarpieceRoute); 1510 mRouteCodeToQuiescentState.put(ROUTE_BLUETOOTH, mQuiescentBluetoothRoute); 1511 mRouteCodeToQuiescentState.put(ROUTE_SPEAKER, mQuiescentSpeakerRoute); 1512 mRouteCodeToQuiescentState.put(ROUTE_WIRED_HEADSET, mQuiescentHeadsetRoute); 1513 } 1514 setCallAudioManager(CallAudioManager callAudioManager)1515 public void setCallAudioManager(CallAudioManager callAudioManager) { 1516 mCallAudioManager = callAudioManager; 1517 } 1518 1519 /** 1520 * Initializes the state machine with info on initial audio route, supported audio routes, 1521 * and mute status. 1522 */ initialize()1523 public void initialize() { 1524 CallAudioState initState = getInitialAudioState(); 1525 initialize(initState); 1526 } 1527 initialize(CallAudioState initState)1528 public void initialize(CallAudioState initState) { 1529 if ((initState.getRoute() & getCurrentCallSupportedRoutes()) == 0) { 1530 Log.e(this, new IllegalArgumentException(), "Route %d specified when supported call" + 1531 " routes are: %d", initState.getRoute(), getCurrentCallSupportedRoutes()); 1532 } 1533 1534 mCurrentCallAudioState = initState; 1535 mLastKnownCallAudioState = initState; 1536 mDeviceSupportedRoutes = initState.getSupportedRouteMask(); 1537 mAvailableRoutes = mDeviceSupportedRoutes & getCurrentCallSupportedRoutes(); 1538 mIsMuted = initState.isMuted(); 1539 mWasOnSpeaker = false; 1540 mContext.registerReceiver(mMuteChangeReceiver, 1541 new IntentFilter(AudioManager.ACTION_MICROPHONE_MUTE_CHANGED)); 1542 mContext.registerReceiver(mMuteChangeReceiver, 1543 new IntentFilter(AudioManager.STREAM_MUTE_CHANGED_ACTION)); 1544 mContext.registerReceiver(mSpeakerPhoneChangeReceiver, 1545 new IntentFilter(AudioManager.ACTION_SPEAKERPHONE_STATE_CHANGED)); 1546 1547 mStatusBarNotifier.notifyMute(initState.isMuted()); 1548 mStatusBarNotifier.notifySpeakerphone(initState.getRoute() == CallAudioState.ROUTE_SPEAKER); 1549 setInitialState(mRouteCodeToQuiescentState.get(initState.getRoute())); 1550 start(); 1551 } 1552 1553 /** 1554 * Getter for the current CallAudioState object that the state machine is keeping track of. 1555 * Used for compatibility purposes. 1556 */ getCurrentCallAudioState()1557 public CallAudioState getCurrentCallAudioState() { 1558 return mCurrentCallAudioState; 1559 } 1560 sendMessageWithSessionInfo(int message, int arg)1561 public void sendMessageWithSessionInfo(int message, int arg) { 1562 sendMessageWithSessionInfo(message, arg, null); 1563 } 1564 sendMessageWithSessionInfo(int message)1565 public void sendMessageWithSessionInfo(int message) { 1566 sendMessageWithSessionInfo(message, 0, null); 1567 } 1568 sendMessageWithSessionInfo(int message, int arg, String data)1569 public void sendMessageWithSessionInfo(int message, int arg, String data) { 1570 SomeArgs args = SomeArgs.obtain(); 1571 args.arg1 = Log.createSubsession(); 1572 args.arg2 = data; 1573 sendMessage(message, arg, 0, args); 1574 } 1575 1576 /** 1577 * This is for state-independent changes in audio route (i.e. muting or runnables) 1578 * @param msg that couldn't be handled. 1579 */ 1580 @Override unhandledMessage(Message msg)1581 protected void unhandledMessage(Message msg) { 1582 switch (msg.what) { 1583 case MUTE_ON: 1584 setMuteOn(true); 1585 updateSystemMuteState(); 1586 return; 1587 case MUTE_OFF: 1588 setMuteOn(false); 1589 updateSystemMuteState(); 1590 return; 1591 case MUTE_EXTERNALLY_CHANGED: 1592 mIsMuted = mAudioManager.isMicrophoneMute(); 1593 if (isInActiveState()) { 1594 updateSystemMuteState(); 1595 } 1596 return; 1597 case TOGGLE_MUTE: 1598 if (mIsMuted) { 1599 sendInternalMessage(MUTE_OFF); 1600 } else { 1601 sendInternalMessage(MUTE_ON); 1602 } 1603 return; 1604 case UPDATE_SYSTEM_AUDIO_ROUTE: 1605 updateInternalCallAudioState(); 1606 updateRouteForForegroundCall(); 1607 resendSystemAudioState(); 1608 return; 1609 case RUN_RUNNABLE: 1610 java.lang.Runnable r = (java.lang.Runnable) msg.obj; 1611 r.run(); 1612 return; 1613 default: 1614 Log.e(this, new IllegalStateException(), "Unexpected message code %d", msg.what); 1615 } 1616 } 1617 quitStateMachine()1618 public void quitStateMachine() { 1619 quitNow(); 1620 } 1621 dumpPendingMessages(IndentingPrintWriter pw)1622 public void dumpPendingMessages(IndentingPrintWriter pw) { 1623 getHandler().getLooper().dump(pw::println, ""); 1624 } 1625 isHfpDeviceAvailable()1626 public boolean isHfpDeviceAvailable() { 1627 return mBluetoothRouteManager.isBluetoothAvailable(); 1628 } 1629 setSpeakerphoneOn(boolean on)1630 private void setSpeakerphoneOn(boolean on) { 1631 Log.i(this, "turning speaker phone %s", on); 1632 mAudioManager.setSpeakerphoneOn(on); 1633 mStatusBarNotifier.notifySpeakerphone(on); 1634 } 1635 setBluetoothOn(String address)1636 private void setBluetoothOn(String address) { 1637 if (mBluetoothRouteManager.isBluetoothAvailable()) { 1638 BluetoothDevice connectedDevice = 1639 mBluetoothRouteManager.getBluetoothAudioConnectedDevice(); 1640 if (address == null && connectedDevice != null) { 1641 // null means connect to any device, so if we're already connected to some device, 1642 // that means we can just tell ourselves that it's connected. 1643 // Do still try to connect audio though, so that BluetoothRouteManager knows that 1644 // there's an active call. 1645 Log.i(this, "Bluetooth audio already on."); 1646 sendInternalMessage(BT_AUDIO_CONNECTED); 1647 mBluetoothRouteManager.connectBluetoothAudio(connectedDevice.getAddress()); 1648 return; 1649 } 1650 if (connectedDevice == null || !Objects.equals(address, connectedDevice.getAddress())) { 1651 Log.i(this, "connecting bluetooth audio: %s", address); 1652 mBluetoothRouteManager.connectBluetoothAudio(address); 1653 } 1654 } 1655 } 1656 setBluetoothOff()1657 private void setBluetoothOff() { 1658 if (mBluetoothRouteManager.isBluetoothAvailable()) { 1659 if (mBluetoothRouteManager.isBluetoothAudioConnectedOrPending()) { 1660 Log.i(this, "disconnecting bluetooth audio"); 1661 mBluetoothRouteManager.disconnectBluetoothAudio(); 1662 } 1663 } 1664 } 1665 setMuteOn(boolean mute)1666 private void setMuteOn(boolean mute) { 1667 mIsMuted = mute; 1668 Log.addEvent(mCallsManager.getForegroundCall(), mute ? 1669 LogUtils.Events.MUTE : LogUtils.Events.UNMUTE); 1670 if (mute != mAudioManager.isMicrophoneMute() && isInActiveState()) { 1671 IAudioService audio = mAudioServiceFactory.getAudioService(); 1672 Log.i(this, "changing microphone mute state to: %b [serviceIsNull=%b]", 1673 mute, audio == null); 1674 if (audio != null) { 1675 try { 1676 // We use the audio service directly here so that we can specify 1677 // the current user. Telecom runs in the system_server process which 1678 // may run as a separate user from the foreground user. If we 1679 // used AudioManager directly, we would change mute for the system's 1680 // user and not the current foreground, which we want to avoid. 1681 audio.setMicrophoneMute( 1682 mute, mContext.getOpPackageName(), getCurrentUserId()); 1683 } catch (RemoteException e) { 1684 Log.e(this, e, "Remote exception while toggling mute."); 1685 } 1686 // TODO: Check microphone state after attempting to set to ensure that 1687 // our state corroborates AudioManager's state. 1688 } 1689 } 1690 } 1691 updateSystemMuteState()1692 private void updateSystemMuteState() { 1693 CallAudioState newCallAudioState = new CallAudioState(mIsMuted, 1694 mCurrentCallAudioState.getRoute(), 1695 mAvailableRoutes, 1696 mCurrentCallAudioState.getActiveBluetoothDevice(), 1697 mBluetoothRouteManager.getConnectedDevices()); 1698 setSystemAudioState(newCallAudioState); 1699 updateInternalCallAudioState(); 1700 } 1701 1702 /** 1703 * Updates the CallAudioState object from current internal state. The result is used for 1704 * external communication only. 1705 */ updateInternalCallAudioState()1706 private void updateInternalCallAudioState() { 1707 IState currentState = getCurrentState(); 1708 if (currentState == null) { 1709 Log.e(this, new IllegalStateException(), "Current state should never be null" + 1710 " when updateInternalCallAudioState is called."); 1711 mCurrentCallAudioState = new CallAudioState( 1712 mIsMuted, mCurrentCallAudioState.getRoute(), mAvailableRoutes, 1713 mBluetoothRouteManager.getBluetoothAudioConnectedDevice(), 1714 mBluetoothRouteManager.getConnectedDevices()); 1715 return; 1716 } 1717 int currentRoute = mStateNameToRouteCode.get(currentState.getName()); 1718 mCurrentCallAudioState = new CallAudioState(mIsMuted, currentRoute, mAvailableRoutes, 1719 mBluetoothRouteManager.getBluetoothAudioConnectedDevice(), 1720 mBluetoothRouteManager.getConnectedDevices()); 1721 } 1722 setSystemAudioState(CallAudioState newCallAudioState)1723 private void setSystemAudioState(CallAudioState newCallAudioState) { 1724 setSystemAudioState(newCallAudioState, false); 1725 } 1726 resendSystemAudioState()1727 private void resendSystemAudioState() { 1728 setSystemAudioState(mLastKnownCallAudioState, true); 1729 } 1730 setSystemAudioState(CallAudioState newCallAudioState, boolean force)1731 private void setSystemAudioState(CallAudioState newCallAudioState, boolean force) { 1732 synchronized (mLock) { 1733 Log.i(this, "setSystemAudioState: changing from %s to %s", mLastKnownCallAudioState, 1734 newCallAudioState); 1735 if (force || !newCallAudioState.equals(mLastKnownCallAudioState)) { 1736 mStatusBarNotifier.notifyMute(newCallAudioState.isMuted()); 1737 mCallsManager.onCallAudioStateChanged(mLastKnownCallAudioState, newCallAudioState); 1738 updateAudioForForegroundCall(newCallAudioState); 1739 mLastKnownCallAudioState = newCallAudioState; 1740 } 1741 } 1742 } 1743 updateAudioForForegroundCall(CallAudioState newCallAudioState)1744 private void updateAudioForForegroundCall(CallAudioState newCallAudioState) { 1745 Call call = mCallsManager.getForegroundCall(); 1746 if (call != null && call.getConnectionService() != null) { 1747 call.getConnectionService().onCallAudioStateChanged(call, newCallAudioState); 1748 } 1749 } 1750 calculateSupportedRoutes()1751 private int calculateSupportedRoutes() { 1752 int routeMask = CallAudioState.ROUTE_SPEAKER; 1753 1754 if (mWiredHeadsetManager.isPluggedIn()) { 1755 routeMask |= CallAudioState.ROUTE_WIRED_HEADSET; 1756 } else if (mDoesDeviceSupportEarpieceRoute){ 1757 routeMask |= CallAudioState.ROUTE_EARPIECE; 1758 } 1759 1760 if (mBluetoothRouteManager.isBluetoothAvailable()) { 1761 routeMask |= CallAudioState.ROUTE_BLUETOOTH; 1762 } 1763 1764 return routeMask; 1765 } 1766 sendInternalMessage(int messageCode)1767 private void sendInternalMessage(int messageCode) { 1768 sendInternalMessage(messageCode, 0); 1769 } 1770 sendInternalMessage(int messageCode, int arg1)1771 private void sendInternalMessage(int messageCode, int arg1) { 1772 // Internal messages are messages which the state machine sends to itself in the 1773 // course of processing externally-sourced messages. We want to send these messages at 1774 // the front of the queue in order to make actions appear atomic to the user and to 1775 // prevent scenarios such as these: 1776 // 1. State machine handler thread is suspended for some reason. 1777 // 2. Headset gets connected (sends CONNECT_HEADSET). 1778 // 3. User switches to speakerphone in the UI (sends SWITCH_SPEAKER). 1779 // 4. State machine handler is un-suspended. 1780 // 5. State machine handler processes the CONNECT_HEADSET message and sends 1781 // SWITCH_HEADSET at end of queue. 1782 // 6. State machine handler processes SWITCH_SPEAKER. 1783 // 7. State machine handler processes SWITCH_HEADSET. 1784 Session subsession = Log.createSubsession(); 1785 if(subsession != null) { 1786 SomeArgs args = SomeArgs.obtain(); 1787 args.arg1 = subsession; 1788 sendMessageAtFrontOfQueue(messageCode, arg1, 0, args); 1789 } else { 1790 sendMessageAtFrontOfQueue(messageCode, arg1); 1791 } 1792 } 1793 getInitialAudioState()1794 private CallAudioState getInitialAudioState() { 1795 int supportedRouteMask = calculateSupportedRoutes() & getCurrentCallSupportedRoutes(); 1796 final int route; 1797 1798 if ((supportedRouteMask & ROUTE_BLUETOOTH) != 0 1799 && mBluetoothRouteManager.hasBtActiveDevice()) { 1800 route = ROUTE_BLUETOOTH; 1801 } else if ((supportedRouteMask & ROUTE_WIRED_HEADSET) != 0) { 1802 route = ROUTE_WIRED_HEADSET; 1803 } else if ((supportedRouteMask & ROUTE_EARPIECE) != 0) { 1804 route = ROUTE_EARPIECE; 1805 } else { 1806 route = ROUTE_SPEAKER; 1807 } 1808 1809 return new CallAudioState(false, route, supportedRouteMask, null, 1810 mBluetoothRouteManager.getConnectedDevices()); 1811 } 1812 getCurrentUserId()1813 private int getCurrentUserId() { 1814 final long ident = Binder.clearCallingIdentity(); 1815 try { 1816 UserInfo currentUser = ActivityManager.getService().getCurrentUser(); 1817 return currentUser.id; 1818 } catch (RemoteException e) { 1819 // Activity manager not running, nothing we can do assume user 0. 1820 } finally { 1821 Binder.restoreCallingIdentity(ident); 1822 } 1823 return UserHandle.USER_OWNER; 1824 } 1825 isInActiveState()1826 public boolean isInActiveState() { 1827 AudioState currentState = (AudioState) getCurrentState(); 1828 if (currentState == null) { 1829 Log.w(this, "Current state is null, assuming inactive state"); 1830 return false; 1831 } 1832 return currentState.isActive(); 1833 } 1834 checkForEarpieceSupport()1835 private boolean checkForEarpieceSupport() { 1836 AudioDeviceInfo[] deviceList = mAudioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS); 1837 for (AudioDeviceInfo device: deviceList) { 1838 if (device.getType() == AudioDeviceInfo.TYPE_BUILTIN_EARPIECE) { 1839 return true; 1840 } 1841 } 1842 // No earpiece found 1843 return false; 1844 } 1845 calculateBaselineRouteMessage(boolean isExplicitUserRequest, boolean includeBluetooth)1846 private int calculateBaselineRouteMessage(boolean isExplicitUserRequest, 1847 boolean includeBluetooth) { 1848 boolean isSkipEarpiece = false; 1849 if (!isExplicitUserRequest) { 1850 synchronized (mLock) { 1851 // Check video calls to skip earpiece since the baseline for video 1852 // calls should be the speakerphone route 1853 isSkipEarpiece = mCallsManager.hasVideoCall(); 1854 } 1855 } 1856 if ((mAvailableRoutes & ROUTE_BLUETOOTH) != 0 1857 && !mHasUserExplicitlyLeftBluetooth 1858 && includeBluetooth) { 1859 return isExplicitUserRequest ? USER_SWITCH_BLUETOOTH : SWITCH_BLUETOOTH; 1860 } else if ((mAvailableRoutes & ROUTE_EARPIECE) != 0 && !isSkipEarpiece) { 1861 return isExplicitUserRequest ? USER_SWITCH_EARPIECE : SWITCH_EARPIECE; 1862 } else if ((mAvailableRoutes & ROUTE_WIRED_HEADSET) != 0) { 1863 return isExplicitUserRequest ? USER_SWITCH_HEADSET : SWITCH_HEADSET; 1864 } else { 1865 return isExplicitUserRequest ? USER_SWITCH_SPEAKER : SWITCH_SPEAKER; 1866 } 1867 } 1868 reinitialize()1869 private void reinitialize() { 1870 CallAudioState initState = getInitialAudioState(); 1871 mDeviceSupportedRoutes = initState.getSupportedRouteMask(); 1872 mAvailableRoutes = mDeviceSupportedRoutes & getCurrentCallSupportedRoutes(); 1873 mIsMuted = initState.isMuted(); 1874 setSpeakerphoneOn(initState.getRoute() == CallAudioState.ROUTE_SPEAKER); 1875 setMuteOn(mIsMuted); 1876 mWasOnSpeaker = false; 1877 mHasUserExplicitlyLeftBluetooth = false; 1878 mLastKnownCallAudioState = initState; 1879 transitionTo(mRouteCodeToQuiescentState.get(initState.getRoute())); 1880 } 1881 updateRouteForForegroundCall()1882 private void updateRouteForForegroundCall() { 1883 mAvailableRoutes = mDeviceSupportedRoutes & getCurrentCallSupportedRoutes(); 1884 1885 CallAudioState currentState = getCurrentCallAudioState(); 1886 1887 // Move to baseline route in the case the current route is no longer available. 1888 if ((mAvailableRoutes & currentState.getRoute()) == 0) { 1889 sendInternalMessage(calculateBaselineRouteMessage(false, true)); 1890 } 1891 } 1892 getCurrentCallSupportedRoutes()1893 private int getCurrentCallSupportedRoutes() { 1894 int supportedRoutes = CallAudioState.ROUTE_ALL; 1895 1896 if (mCallsManager.getForegroundCall() != null) { 1897 supportedRoutes &= mCallsManager.getForegroundCall().getSupportedAudioRoutes(); 1898 } 1899 1900 return supportedRoutes; 1901 } 1902 modifyRoutes(int base, int remove, int add, boolean considerCurrentCall)1903 private int modifyRoutes(int base, int remove, int add, boolean considerCurrentCall) { 1904 base &= ~remove; 1905 1906 if (considerCurrentCall) { 1907 add &= getCurrentCallSupportedRoutes(); 1908 } 1909 1910 base |= add; 1911 1912 return base; 1913 } 1914 } 1915