1 /* 2 * Copyright (C) 2012 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.bluetooth.hfp; 18 19 import static android.Manifest.permission.BLUETOOTH_CONNECT; 20 import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED; 21 import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; 22 import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_UNKNOWN; 23 import static android.bluetooth.BluetoothProfile.STATE_CONNECTED; 24 import static android.bluetooth.BluetoothProfile.STATE_CONNECTING; 25 import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED; 26 import static android.media.audio.Flags.deprecateStreamBtSco; 27 28 import static com.android.modules.utils.build.SdkLevel.isAtLeastU; 29 30 import static java.util.Objects.requireNonNull; 31 32 import android.annotation.NonNull; 33 import android.annotation.Nullable; 34 import android.bluetooth.BluetoothDevice; 35 import android.bluetooth.BluetoothHeadset; 36 import android.bluetooth.BluetoothProfile; 37 import android.bluetooth.BluetoothSinkAudioPolicy; 38 import android.bluetooth.BluetoothStatusCodes; 39 import android.bluetooth.BluetoothUuid; 40 import android.content.BroadcastReceiver; 41 import android.content.Context; 42 import android.content.Intent; 43 import android.content.IntentFilter; 44 import android.media.AudioDeviceCallback; 45 import android.media.AudioDeviceInfo; 46 import android.media.AudioManager; 47 import android.media.BluetoothProfileConnectionInfo; 48 import android.net.Uri; 49 import android.os.BatteryManager; 50 import android.os.Binder; 51 import android.os.Handler; 52 import android.os.HandlerThread; 53 import android.os.Looper; 54 import android.os.ParcelUuid; 55 import android.os.UserHandle; 56 import android.sysprop.BluetoothProperties; 57 import android.telecom.PhoneAccount; 58 import android.util.Log; 59 60 import com.android.bluetooth.BluetoothStatsLog; 61 import com.android.bluetooth.Utils; 62 import com.android.bluetooth.a2dp.A2dpService; 63 import com.android.bluetooth.btservice.AdapterService; 64 import com.android.bluetooth.btservice.MetricsLogger; 65 import com.android.bluetooth.btservice.ProfileService; 66 import com.android.bluetooth.btservice.ServiceFactory; 67 import com.android.bluetooth.btservice.storage.DatabaseManager; 68 import com.android.bluetooth.flags.Flags; 69 import com.android.bluetooth.hfpclient.HeadsetClientService; 70 import com.android.bluetooth.hfpclient.HeadsetClientStateMachine; 71 import com.android.bluetooth.le_audio.LeAudioService; 72 import com.android.bluetooth.telephony.BluetoothInCallService; 73 import com.android.bluetooth.util.SystemProperties; 74 import com.android.internal.annotations.VisibleForTesting; 75 76 import java.util.ArrayList; 77 import java.util.Arrays; 78 import java.util.Comparator; 79 import java.util.HashMap; 80 import java.util.List; 81 import java.util.Objects; 82 import java.util.Optional; 83 84 /** 85 * Provides Bluetooth Headset and Handsfree profile, as a service in the Bluetooth application. 86 * 87 * <p>Three modes for SCO audio: Mode 1: Telecom call through {@link #phoneStateChanged(int, int, 88 * int, String, int, String, boolean)} Mode 2: Virtual call through {@link 89 * #startScoUsingVirtualVoiceCall()} Mode 3: Voice recognition through {@link 90 * #startVoiceRecognition(BluetoothDevice)} 91 * 92 * <p>When one mode is active, other mode cannot be started. API user has to terminate existing 93 * modes using the correct API or just {@link #disconnectAudio()} if user is a system service, 94 * before starting a new mode. 95 * 96 * <p>{@link #connectAudio()} will start SCO audio at one of the above modes, but won't change mode 97 * {@link #disconnectAudio()} can happen in any mode to disconnect SCO 98 * 99 * <p>When audio is disconnected, only Mode 1 Telecom call will be persisted, both Mode 2 virtual 100 * call and Mode 3 voice call will be terminated upon SCO termination and client has to restart the 101 * mode. 102 * 103 * <p>NOTE: SCO termination can either be initiated on the AG side or the HF side TODO(b/79660380): 104 * As a workaround, voice recognition will be terminated if virtual call or Telecom call is 105 * initiated while voice recognition is ongoing, in case calling app did not call {@link 106 * #stopVoiceRecognition(BluetoothDevice)} 107 * 108 * <p>AG - Audio Gateway, device running this {@link HeadsetService}, e.g. Android Phone HF - 109 * Handsfree device, device running headset client, e.g. Wireless headphones or car kits 110 */ 111 public class HeadsetService extends ProfileService { 112 private static final String TAG = HeadsetService.class.getSimpleName(); 113 114 /** HFP AG owned/managed components */ 115 private static final String HFP_AG_IN_CALL_SERVICE = 116 BluetoothInCallService.class.getCanonicalName(); 117 118 private static final String DISABLE_INBAND_RINGING_PROPERTY = 119 "persist.bluetooth.disableinbandringing"; 120 private static final String REJECT_SCO_IF_HFPC_CONNECTED_PROPERTY = 121 "bluetooth.hfp.reject_sco_if_hfpc_connected"; 122 private static final ParcelUuid[] HEADSET_UUIDS = {BluetoothUuid.HSP, BluetoothUuid.HFP}; 123 private static final int[] CONNECTING_CONNECTED_STATES = {STATE_CONNECTING, STATE_CONNECTED}; 124 private static final int DIALING_OUT_TIMEOUT_MS = 10000; 125 private static final int CLCC_END_MARK_INDEX = 0; 126 127 // Timeout for state machine thread join, to prevent potential ANR. 128 private static final int SM_THREAD_JOIN_TIMEOUT_MS = 1000; 129 130 private final AdapterService mAdapterService; 131 private final DatabaseManager mDatabaseManager; 132 private final HeadsetNativeInterface mNativeInterface; 133 private final HashMap<BluetoothDevice, HeadsetStateMachine> mStateMachines = new HashMap<>(); 134 private final Handler mHandler; 135 private final Looper mStateMachinesLooper; 136 private final Handler mStateMachinesThreadHandler; 137 private final HandlerThread mStateMachinesThread; 138 // This is also used as a lock for shared data in HeadsetService 139 private final HeadsetSystemInterface mSystemInterface; 140 141 private int mMaxHeadsetConnections = 1; 142 private BluetoothDevice mExposedActiveDevice; 143 private BluetoothDevice mActiveDevice; 144 private boolean mAudioRouteAllowed = true; 145 // Indicates whether SCO audio needs to be forced to open regardless ANY OTHER restrictions 146 private boolean mForceScoAudio; 147 private boolean mInbandRingingRuntimeDisable; 148 private boolean mVirtualCallStarted; 149 // Non null value indicates a pending dialing out event is going on 150 private DialingOutTimeoutEvent mDialingOutTimeoutEvent; 151 private boolean mVoiceRecognitionStarted; 152 // Non null value indicates a pending voice recognition request from headset is going on 153 private VoiceRecognitionTimeoutEvent mVoiceRecognitionTimeoutEvent; 154 // Timeout when voice recognition is started by remote device 155 @VisibleForTesting static int sStartVrTimeoutMs = 5000; 156 private final ArrayList<StateMachineTask> mPendingClccResponses = new ArrayList<>(); 157 private final AudioManagerAudioDeviceCallback mAudioManagerAudioDeviceCallback = 158 new AudioManagerAudioDeviceCallback(); 159 private static HeadsetService sHeadsetService; 160 161 @VisibleForTesting boolean mIsAptXSwbEnabled = false; 162 @VisibleForTesting boolean mIsAptXSwbPmEnabled = false; 163 164 @VisibleForTesting ServiceFactory mFactory = new ServiceFactory(); 165 HeadsetService(AdapterService adapterService)166 public HeadsetService(AdapterService adapterService) { 167 this(adapterService, HeadsetNativeInterface.getInstance(), null); 168 } 169 170 @VisibleForTesting HeadsetService(AdapterService adapterService, HeadsetNativeInterface nativeInterface)171 HeadsetService(AdapterService adapterService, HeadsetNativeInterface nativeInterface) { 172 this(adapterService, nativeInterface, null); 173 } 174 175 @VisibleForTesting HeadsetService( AdapterService adapterService, HeadsetNativeInterface nativeInterface, Looper looper)176 HeadsetService( 177 AdapterService adapterService, HeadsetNativeInterface nativeInterface, Looper looper) { 178 super(requireNonNull(adapterService)); 179 mAdapterService = adapterService; 180 mDatabaseManager = requireNonNull(mAdapterService.getDatabase()); 181 mNativeInterface = requireNonNull(nativeInterface); 182 if (looper != null) { 183 mHandler = new Handler(looper); 184 mStateMachinesThread = null; 185 mStateMachinesLooper = looper; 186 } else { 187 mHandler = new Handler(Looper.getMainLooper()); 188 mStateMachinesThread = new HandlerThread("HeadsetService.StateMachines"); 189 mStateMachinesThread.start(); 190 mStateMachinesLooper = mStateMachinesThread.getLooper(); 191 } 192 mStateMachinesThreadHandler = new Handler(mStateMachinesLooper); 193 194 setComponentAvailable(HFP_AG_IN_CALL_SERVICE, true); 195 196 // Step 3: Initialize system interface 197 mSystemInterface = HeadsetObjectsFactory.getInstance().makeSystemInterface(this); 198 // Step 4: Initialize native interface 199 mIsAptXSwbEnabled = 200 SystemProperties.getBoolean("bluetooth.hfp.codec_aptx_voice.enabled", false); 201 Log.i(TAG, "mIsAptXSwbEnabled: " + mIsAptXSwbEnabled); 202 mIsAptXSwbPmEnabled = 203 SystemProperties.getBoolean( 204 "bluetooth.hfp.swb.aptx.power_management.enabled", false); 205 Log.i(TAG, "mIsAptXSwbPmEnabled: " + mIsAptXSwbPmEnabled); 206 setHeadsetService(this); 207 mMaxHeadsetConnections = mAdapterService.getMaxConnectedAudioDevices(); 208 // Add 1 to allow a pending device to be connecting or disconnecting 209 mNativeInterface.init(mMaxHeadsetConnections + 1, isInbandRingingEnabled()); 210 enableSwbCodec( 211 HeadsetHalConstants.BTHF_SWB_CODEC_VENDOR_APTX, mIsAptXSwbEnabled, mActiveDevice); 212 // Step 6: Register Audio Device callback 213 if (Utils.isScoManagedByAudioEnabled()) { 214 mSystemInterface 215 .getAudioManager() 216 .registerAudioDeviceCallback(mAudioManagerAudioDeviceCallback, mHandler); 217 } 218 219 // Step 7: Setup broadcast receivers 220 IntentFilter filter = new IntentFilter(); 221 filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); 222 filter.addAction(Intent.ACTION_BATTERY_CHANGED); 223 filter.addAction(AudioManager.ACTION_VOLUME_CHANGED); 224 filter.addAction(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY); 225 registerReceiver(mHeadsetReceiver, filter); 226 } 227 isEnabled()228 public static boolean isEnabled() { 229 return BluetoothProperties.isProfileHfpAgEnabled().orElse(false); 230 } 231 232 @Override initBinder()233 public IProfileServiceBinder initBinder() { 234 return new HeadsetServiceBinder(this); 235 } 236 237 @Override cleanup()238 public void cleanup() { 239 Log.i(TAG, "Cleanup Headset Service"); 240 241 // Step 7: Tear down broadcast receivers 242 unregisterReceiver(mHeadsetReceiver); 243 244 // Step 6: Unregister Audio Device Callback 245 if (Utils.isScoManagedByAudioEnabled()) { 246 mSystemInterface 247 .getAudioManager() 248 .unregisterAudioDeviceCallback(mAudioManagerAudioDeviceCallback); 249 } 250 251 synchronized (mStateMachines) { 252 // Reset active device to null 253 if (mActiveDevice != null) { 254 mExposedActiveDevice = null; 255 mActiveDevice = null; 256 broadcastActiveDevice(null); 257 } 258 mInbandRingingRuntimeDisable = false; 259 mForceScoAudio = false; 260 mAudioRouteAllowed = true; 261 mMaxHeadsetConnections = 1; 262 mVoiceRecognitionStarted = false; 263 mVirtualCallStarted = false; 264 if (mDialingOutTimeoutEvent != null) { 265 mStateMachinesThreadHandler.removeCallbacks(mDialingOutTimeoutEvent); 266 mDialingOutTimeoutEvent = null; 267 } 268 if (mVoiceRecognitionTimeoutEvent != null) { 269 mStateMachinesThreadHandler.removeCallbacks(mVoiceRecognitionTimeoutEvent); 270 mVoiceRecognitionTimeoutEvent = null; 271 if (mSystemInterface.getVoiceRecognitionWakeLock().isHeld()) { 272 try { 273 mSystemInterface.getVoiceRecognitionWakeLock().release(); 274 } catch (RuntimeException e) { 275 Log.d(TAG, "non properly release getVoiceRecognitionWakeLock", e); 276 } 277 } 278 } 279 // Step 5: Destroy state machines 280 for (HeadsetStateMachine stateMachine : mStateMachines.values()) { 281 HeadsetObjectsFactory.getInstance().destroyStateMachine(stateMachine); 282 } 283 mStateMachines.clear(); 284 } 285 // Step 4: Destroy native interface 286 mNativeInterface.cleanup(); 287 setHeadsetService(null); 288 // Step 3: Destroy system interface 289 mSystemInterface.stop(); 290 // Step 2: Stop handler thread 291 if (mStateMachinesThread != null) { 292 try { 293 mStateMachinesThread.quitSafely(); 294 mStateMachinesThread.join(SM_THREAD_JOIN_TIMEOUT_MS); 295 } catch (InterruptedException e) { 296 // Do not rethrow as we are shutting down anyway 297 } 298 } 299 300 // Unregister Handler and stop all queued messages. 301 mHandler.removeCallbacksAndMessages(null); 302 303 // Step 1: Clear 304 setComponentAvailable(HFP_AG_IN_CALL_SERVICE, false); 305 } 306 307 /** 308 * Checks if this service object is able to accept binder calls 309 * 310 * @return True if the object can accept binder calls, False otherwise 311 */ isAlive()312 public boolean isAlive() { 313 return isAvailable(); 314 } 315 316 /** 317 * Get the {@link Looper} for the state machine thread. This is used in testing and helper 318 * objects 319 * 320 * @return {@link Looper} for the state machine thread 321 */ 322 @VisibleForTesting getStateMachinesThreadLooper()323 public Looper getStateMachinesThreadLooper() { 324 return mStateMachinesThread.getLooper(); 325 } 326 327 interface StateMachineTask { execute(HeadsetStateMachine stateMachine)328 void execute(HeadsetStateMachine stateMachine); 329 } 330 doForStateMachine(BluetoothDevice device, StateMachineTask task)331 private boolean doForStateMachine(BluetoothDevice device, StateMachineTask task) { 332 synchronized (mStateMachines) { 333 HeadsetStateMachine stateMachine = mStateMachines.get(device); 334 if (stateMachine == null) { 335 return false; 336 } 337 task.execute(stateMachine); 338 } 339 return true; 340 } 341 doForEachConnectedStateMachine(StateMachineTask task)342 private void doForEachConnectedStateMachine(StateMachineTask task) { 343 synchronized (mStateMachines) { 344 for (BluetoothDevice device : getConnectedDevices()) { 345 task.execute(mStateMachines.get(device)); 346 } 347 } 348 } 349 doForEachConnectedStateMachine(List<StateMachineTask> tasks)350 private void doForEachConnectedStateMachine(List<StateMachineTask> tasks) { 351 synchronized (mStateMachines) { 352 for (BluetoothDevice device : getConnectedDevices()) { 353 for (StateMachineTask task : tasks) { 354 task.execute(mStateMachines.get(device)); 355 } 356 } 357 } 358 } 359 onDeviceStateChanged(HeadsetDeviceState deviceState)360 void onDeviceStateChanged(HeadsetDeviceState deviceState) { 361 doForEachConnectedStateMachine( 362 stateMachine -> 363 stateMachine.sendMessage( 364 HeadsetStateMachine.DEVICE_STATE_CHANGED, deviceState)); 365 } 366 367 /** 368 * Handle messages from native (JNI) to Java. This needs to be synchronized to avoid posting 369 * messages to state machine before start() is done 370 * 371 * @param stackEvent event from native stack 372 */ messageFromNative(HeadsetStackEvent stackEvent)373 void messageFromNative(HeadsetStackEvent stackEvent) { 374 requireNonNull(stackEvent.device); 375 synchronized (mStateMachines) { 376 HeadsetStateMachine stateMachine = mStateMachines.get(stackEvent.device); 377 if (stackEvent.type == HeadsetStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED) { 378 switch (stackEvent.valueInt) { 379 case HeadsetHalConstants.CONNECTION_STATE_CONNECTED: 380 case HeadsetHalConstants.CONNECTION_STATE_CONNECTING: 381 { 382 // Create new state machine if none is found 383 if (stateMachine == null) { 384 stateMachine = 385 HeadsetObjectsFactory.getInstance() 386 .makeStateMachine( 387 stackEvent.device, 388 mStateMachinesLooper, 389 this, 390 mAdapterService, 391 mNativeInterface, 392 mSystemInterface); 393 mStateMachines.put(stackEvent.device, stateMachine); 394 } 395 break; 396 } 397 } 398 } 399 if (stateMachine == null) { 400 throw new IllegalStateException( 401 "State machine not found for stack event: " + stackEvent); 402 } 403 stateMachine.sendMessage(HeadsetStateMachine.STACK_EVENT, stackEvent); 404 } 405 } 406 407 private final BroadcastReceiver mHeadsetReceiver = 408 new BroadcastReceiver() { 409 @Override 410 public void onReceive(Context context, Intent intent) { 411 String action = intent.getAction(); 412 if (action == null) { 413 Log.w(TAG, "mHeadsetReceiver, action is null"); 414 return; 415 } 416 switch (action) { 417 case Intent.ACTION_BATTERY_CHANGED: 418 { 419 int batteryLevel = 420 intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1); 421 int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1); 422 if (batteryLevel < 0 || scale <= 0) { 423 Log.e( 424 TAG, 425 "Bad Battery Changed intent: batteryLevel=" 426 + batteryLevel 427 + ", scale=" 428 + scale); 429 return; 430 } 431 int cindBatteryLevel = 432 Math.round(batteryLevel * 5 / ((float) scale)); 433 mSystemInterface 434 .getHeadsetPhoneState() 435 .setCindBatteryCharge(cindBatteryLevel); 436 break; 437 } 438 case AudioManager.ACTION_VOLUME_CHANGED: 439 { 440 int streamType = 441 intent.getIntExtra( 442 AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1); 443 int volStream = AudioManager.STREAM_BLUETOOTH_SCO; 444 if (deprecateStreamBtSco()) { 445 volStream = AudioManager.STREAM_VOICE_CALL; 446 } 447 if (streamType == volStream) { 448 doForEachConnectedStateMachine( 449 stateMachine -> 450 stateMachine.sendMessage( 451 HeadsetStateMachine 452 .INTENT_SCO_VOLUME_CHANGED, 453 intent)); 454 } 455 break; 456 } 457 case BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY: 458 { 459 int requestType = 460 intent.getIntExtra( 461 BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE, 462 BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS); 463 BluetoothDevice device = 464 intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 465 logD( 466 "Received BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY," 467 + " device=" 468 + device 469 + ", type=" 470 + requestType); 471 if (requestType == BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS) { 472 synchronized (mStateMachines) { 473 final HeadsetStateMachine stateMachine = 474 mStateMachines.get(device); 475 if (stateMachine == null) { 476 Log.wtf(TAG, "Cannot find state machine for " + device); 477 return; 478 } 479 stateMachine.sendMessage( 480 HeadsetStateMachine.INTENT_CONNECTION_ACCESS_REPLY, 481 intent); 482 } 483 } 484 break; 485 } 486 default: 487 Log.w(TAG, "Unknown action " + action); 488 } 489 } 490 }; 491 handleBondStateChanged(BluetoothDevice device, int fromState, int toState)492 public void handleBondStateChanged(BluetoothDevice device, int fromState, int toState) { 493 mHandler.post(() -> bondStateChanged(device, toState)); 494 } 495 bondStateChanged(BluetoothDevice device, int state)496 private void bondStateChanged(BluetoothDevice device, int state) { 497 logD("Bond state changed for device: " + device + " state: " + state); 498 if (state != BluetoothDevice.BOND_NONE) { 499 return; 500 } 501 synchronized (mStateMachines) { 502 HeadsetStateMachine stateMachine = mStateMachines.get(device); 503 if (stateMachine == null) { 504 return; 505 } 506 if (stateMachine.getConnectionState() != STATE_DISCONNECTED) { 507 return; 508 } 509 removeStateMachine(device); 510 } 511 } 512 513 // API methods getHeadsetService()514 public static synchronized HeadsetService getHeadsetService() { 515 if (sHeadsetService == null) { 516 Log.w(TAG, "getHeadsetService(): service is NULL"); 517 return null; 518 } 519 if (!sHeadsetService.isAvailable()) { 520 Log.w(TAG, "getHeadsetService(): service is not available"); 521 return null; 522 } 523 return sHeadsetService; 524 } 525 526 @VisibleForTesting setHeadsetService(HeadsetService instance)527 public static synchronized void setHeadsetService(HeadsetService instance) { 528 logD("setHeadsetService(): set to: " + instance); 529 sHeadsetService = instance; 530 } 531 connect(BluetoothDevice device)532 public boolean connect(BluetoothDevice device) { 533 if (getConnectionPolicy(device) == CONNECTION_POLICY_FORBIDDEN) { 534 Log.w( 535 TAG, 536 "connect: CONNECTION_POLICY_FORBIDDEN, device=" 537 + device 538 + ", " 539 + Utils.getUidPidString()); 540 return false; 541 } 542 final ParcelUuid[] featureUuids = mAdapterService.getRemoteUuids(device); 543 if (!BluetoothUuid.containsAnyUuid(featureUuids, HEADSET_UUIDS)) { 544 Log.e( 545 TAG, 546 "connect: Cannot connect to " 547 + device 548 + ": no headset UUID, " 549 + Utils.getUidPidString()); 550 return false; 551 } 552 synchronized (mStateMachines) { 553 Log.i(TAG, "connect: device=" + device + ", " + Utils.getUidPidString()); 554 HeadsetStateMachine stateMachine = mStateMachines.get(device); 555 if (stateMachine == null) { 556 stateMachine = 557 HeadsetObjectsFactory.getInstance() 558 .makeStateMachine( 559 device, 560 mStateMachinesLooper, 561 this, 562 mAdapterService, 563 mNativeInterface, 564 mSystemInterface); 565 mStateMachines.put(device, stateMachine); 566 } 567 int connectionState = stateMachine.getConnectionState(); 568 if (connectionState == STATE_CONNECTED || connectionState == STATE_CONNECTING) { 569 Log.w( 570 TAG, 571 "connect: device " 572 + device 573 + " is already connected/connecting, connectionState=" 574 + connectionState); 575 return false; 576 } 577 List<BluetoothDevice> connectingConnectedDevices = 578 getDevicesMatchingConnectionStates(CONNECTING_CONNECTED_STATES); 579 boolean disconnectExisting = false; 580 if (connectingConnectedDevices.size() >= mMaxHeadsetConnections) { 581 // When there is maximum one device, we automatically disconnect the current one 582 if (mMaxHeadsetConnections == 1) { 583 disconnectExisting = true; 584 } else { 585 Log.w(TAG, "Max connection has reached, rejecting connection to " + device); 586 return false; 587 } 588 } 589 if (disconnectExisting) { 590 for (BluetoothDevice connectingConnectedDevice : connectingConnectedDevices) { 591 disconnect(connectingConnectedDevice); 592 } 593 setActiveDevice(null); 594 } 595 stateMachine.sendMessage(HeadsetStateMachine.CONNECT, device); 596 } 597 return true; 598 } 599 600 /** 601 * Disconnects hfp from the passed in device 602 * 603 * @param device is the device with which we will disconnect hfp 604 * @return true if hfp is disconnected, false if the device is not connected 605 */ disconnect(BluetoothDevice device)606 public boolean disconnect(BluetoothDevice device) { 607 Log.i(TAG, "disconnect: device=" + device + ", " + Utils.getUidPidString()); 608 synchronized (mStateMachines) { 609 HeadsetStateMachine stateMachine = mStateMachines.get(device); 610 if (stateMachine == null) { 611 Log.w(TAG, "disconnect: device " + device + " not ever connected/connecting"); 612 return false; 613 } 614 int connectionState = stateMachine.getConnectionState(); 615 if (connectionState != STATE_CONNECTED && connectionState != STATE_CONNECTING) { 616 Log.w( 617 TAG, 618 "disconnect: device " 619 + device 620 + " not connected/connecting, connectionState=" 621 + connectionState); 622 return false; 623 } 624 stateMachine.sendMessage(HeadsetStateMachine.DISCONNECT, device); 625 } 626 return true; 627 } 628 getConnectedDevices()629 public List<BluetoothDevice> getConnectedDevices() { 630 ArrayList<BluetoothDevice> devices = new ArrayList<>(); 631 synchronized (mStateMachines) { 632 for (HeadsetStateMachine stateMachine : mStateMachines.values()) { 633 if (stateMachine.getConnectionState() == STATE_CONNECTED) { 634 devices.add(stateMachine.getDevice()); 635 } 636 } 637 } 638 return devices; 639 } 640 641 /** 642 * Same as the API method {@link BluetoothHeadset#getDevicesMatchingConnectionStates(int[])} 643 * 644 * @param states an array of states from {@link BluetoothProfile} 645 * @return a list of devices matching the array of connection states 646 */ getDevicesMatchingConnectionStates(int[] states)647 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 648 ArrayList<BluetoothDevice> devices = new ArrayList<>(); 649 synchronized (mStateMachines) { 650 if (states == null) { 651 return devices; 652 } 653 final BluetoothDevice[] bondedDevices = mAdapterService.getBondedDevices(); 654 if (bondedDevices == null) { 655 return devices; 656 } 657 for (BluetoothDevice device : bondedDevices) { 658 final ParcelUuid[] featureUuids = mAdapterService.getRemoteUuids(device); 659 if (!BluetoothUuid.containsAnyUuid(featureUuids, HEADSET_UUIDS)) { 660 continue; 661 } 662 int connectionState = getConnectionState(device); 663 for (int state : states) { 664 if (connectionState == state) { 665 devices.add(device); 666 break; 667 } 668 } 669 } 670 } 671 return devices; 672 } 673 getConnectionState(BluetoothDevice device)674 public int getConnectionState(BluetoothDevice device) { 675 synchronized (mStateMachines) { 676 final HeadsetStateMachine stateMachine = mStateMachines.get(device); 677 if (stateMachine == null) { 678 return STATE_DISCONNECTED; 679 } 680 return stateMachine.getConnectionState(); 681 } 682 } 683 684 /** 685 * Set connection policy of the profile and connects it if connectionPolicy is {@link 686 * BluetoothProfile#CONNECTION_POLICY_ALLOWED} or disconnects if connectionPolicy is {@link 687 * BluetoothProfile#CONNECTION_POLICY_FORBIDDEN} 688 * 689 * <p>The device should already be paired. Connection policy can be one of: {@link 690 * BluetoothProfile#CONNECTION_POLICY_ALLOWED}, {@link 691 * BluetoothProfile#CONNECTION_POLICY_FORBIDDEN}, {@link 692 * BluetoothProfile#CONNECTION_POLICY_UNKNOWN} 693 * 694 * @param device Paired bluetooth device 695 * @param connectionPolicy is the connection policy to set to for this profile 696 * @return true if connectionPolicy is set, false on error 697 */ setConnectionPolicy(BluetoothDevice device, int connectionPolicy)698 public boolean setConnectionPolicy(BluetoothDevice device, int connectionPolicy) { 699 Log.i( 700 TAG, 701 "setConnectionPolicy: device=" 702 + device 703 + ", connectionPolicy=" 704 + connectionPolicy 705 + ", " 706 + Utils.getUidPidString()); 707 708 if (!mDatabaseManager.setProfileConnectionPolicy( 709 device, BluetoothProfile.HEADSET, connectionPolicy)) { 710 return false; 711 } 712 if (connectionPolicy == CONNECTION_POLICY_ALLOWED) { 713 connect(device); 714 } else if (connectionPolicy == CONNECTION_POLICY_FORBIDDEN) { 715 disconnect(device); 716 } 717 return true; 718 } 719 720 /** 721 * Get the connection policy of the profile. 722 * 723 * <p>The connection policy can be any of: {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED}, 724 * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN}, {@link 725 * BluetoothProfile#CONNECTION_POLICY_UNKNOWN} 726 * 727 * @param device Bluetooth device 728 * @return connection policy of the device 729 */ getConnectionPolicy(BluetoothDevice device)730 public int getConnectionPolicy(BluetoothDevice device) { 731 return mDatabaseManager.getProfileConnectionPolicy(device, BluetoothProfile.HEADSET); 732 } 733 isNoiseReductionSupported(BluetoothDevice device)734 boolean isNoiseReductionSupported(BluetoothDevice device) { 735 return mNativeInterface.isNoiseReductionSupported(device); 736 } 737 isVoiceRecognitionSupported(BluetoothDevice device)738 boolean isVoiceRecognitionSupported(BluetoothDevice device) { 739 return mNativeInterface.isVoiceRecognitionSupported(device); 740 } 741 startVoiceRecognition(BluetoothDevice device)742 boolean startVoiceRecognition(BluetoothDevice device) { 743 Log.i(TAG, "startVoiceRecognition: device=" + device + ", " + Utils.getUidPidString()); 744 synchronized (mStateMachines) { 745 // TODO(b/79660380): Workaround in case voice recognition was not terminated properly 746 if (mVoiceRecognitionStarted) { 747 boolean status = stopVoiceRecognition(mActiveDevice); 748 Log.w( 749 TAG, 750 "startVoiceRecognition: voice recognition is still active, just called " 751 + "stopVoiceRecognition, returned " 752 + status 753 + " on " 754 + mActiveDevice 755 + ", please try again"); 756 mVoiceRecognitionStarted = false; 757 return false; 758 } 759 if (!isAudioModeIdle()) { 760 Log.w( 761 TAG, 762 "startVoiceRecognition: audio mode not idle, active device is " 763 + mActiveDevice); 764 return false; 765 } 766 // Audio should not be on when no audio mode is active 767 if (isAudioOn()) { 768 // Disconnect audio so that API user can try later 769 int status = disconnectAudio(); 770 Log.w( 771 TAG, 772 "startVoiceRecognition: audio is still active, please wait for audio to" 773 + " be disconnected, disconnectAudio() returned " 774 + status 775 + ", active device is " 776 + mActiveDevice); 777 return false; 778 } 779 boolean pendingRequestByHeadset = false; 780 if (mVoiceRecognitionTimeoutEvent != null) { 781 if (!mVoiceRecognitionTimeoutEvent.mVoiceRecognitionDevice.equals(device)) { 782 // TODO(b/79660380): Workaround when target device != requesting device 783 Log.w( 784 TAG, 785 "startVoiceRecognition: device " 786 + device 787 + " is not the same as requesting device " 788 + mVoiceRecognitionTimeoutEvent.mVoiceRecognitionDevice 789 + ", fall back to requesting device"); 790 device = mVoiceRecognitionTimeoutEvent.mVoiceRecognitionDevice; 791 } 792 mStateMachinesThreadHandler.removeCallbacks(mVoiceRecognitionTimeoutEvent); 793 mVoiceRecognitionTimeoutEvent = null; 794 if (mSystemInterface.getVoiceRecognitionWakeLock().isHeld()) { 795 try { 796 mSystemInterface.getVoiceRecognitionWakeLock().release(); 797 } catch (RuntimeException e) { 798 Log.d(TAG, "non properly release getVoiceRecognitionWakeLock", e); 799 } 800 } 801 pendingRequestByHeadset = true; 802 } 803 if (!device.equals(mActiveDevice) && !setActiveDevice(device)) { 804 Log.w(TAG, "startVoiceRecognition: failed to set " + device + " as active"); 805 return false; 806 } 807 final HeadsetStateMachine stateMachine = mStateMachines.get(device); 808 if (stateMachine == null) { 809 Log.w(TAG, "startVoiceRecognition: " + device + " is never connected"); 810 return false; 811 } 812 int connectionState = stateMachine.getConnectionState(); 813 if (connectionState != STATE_CONNECTED && connectionState != STATE_CONNECTING) { 814 Log.w(TAG, "startVoiceRecognition: " + device + " is not connected or connecting"); 815 return false; 816 } 817 if (SystemProperties.getBoolean(REJECT_SCO_IF_HFPC_CONNECTED_PROPERTY, false) 818 && isHeadsetClientConnected()) { 819 Log.w(TAG, "startVoiceRecognition: rejected SCO since HFPC is connected!"); 820 return false; 821 } 822 mVoiceRecognitionStarted = true; 823 if (pendingRequestByHeadset) { 824 stateMachine.sendMessage( 825 HeadsetStateMachine.VOICE_RECOGNITION_RESULT, 1 /* success */, 0, device); 826 } else { 827 stateMachine.sendMessage(HeadsetStateMachine.VOICE_RECOGNITION_START, device); 828 } 829 if (!Utils.isScoManagedByAudioEnabled()) { 830 stateMachine.sendMessage(HeadsetStateMachine.CONNECT_AUDIO, device); 831 logScoSessionMetric( 832 device, 833 BluetoothStatsLog 834 .BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__STATE__SCO_CONNECT_AUDIO_START, 835 Binder.getCallingUid()); 836 } 837 } 838 839 if (Utils.isScoManagedByAudioEnabled()) { 840 BluetoothDevice voiceRecognitionDevice = device; 841 // when isScoManagedByAudio is on, tell AudioManager to connect SCO 842 AudioManager am = mSystemInterface.getAudioManager(); 843 Optional<AudioDeviceInfo> audioDeviceInfo = 844 am.getAvailableCommunicationDevices().stream() 845 .filter( 846 x -> 847 x.getType() == AudioDeviceInfo.TYPE_BLUETOOTH_SCO 848 && x.getAddress() 849 .equals( 850 voiceRecognitionDevice 851 .getAddress())) 852 .findFirst(); 853 if (audioDeviceInfo.isPresent()) { 854 BluetoothDevice finalDevice = device; 855 mHandler.post( 856 () -> { 857 am.setCommunicationDevice(audioDeviceInfo.get()); 858 logScoSessionMetric( 859 finalDevice, 860 BluetoothStatsLog 861 .BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__STATE__SCO_VOICE_RECOGNITION_INITIATED_START, 862 Binder.getCallingUid()); 863 Log.i(TAG, "Audio Manager will initiate the SCO for Voice Recognition"); 864 }); 865 } else { 866 Log.w( 867 TAG, 868 "Cannot find audioDeviceInfo that matches device=" 869 + voiceRecognitionDevice 870 + " to create the SCO"); 871 return false; 872 } 873 } 874 enableSwbCodec(HeadsetHalConstants.BTHF_SWB_CODEC_VENDOR_APTX, true, device); 875 return true; 876 } 877 stopVoiceRecognition(BluetoothDevice device)878 boolean stopVoiceRecognition(BluetoothDevice device) { 879 Log.i(TAG, "stopVoiceRecognition: device=" + device + ", " + Utils.getUidPidString()); 880 synchronized (mStateMachines) { 881 if (!Objects.equals(mActiveDevice, device)) { 882 Log.w( 883 TAG, 884 "stopVoiceRecognition: requested device " 885 + device 886 + " is not active, use active device " 887 + mActiveDevice 888 + " instead"); 889 device = mActiveDevice; 890 } 891 final HeadsetStateMachine stateMachine = mStateMachines.get(device); 892 if (stateMachine == null) { 893 Log.w(TAG, "stopVoiceRecognition: " + device + " is never connected"); 894 return false; 895 } 896 int connectionState = stateMachine.getConnectionState(); 897 if (connectionState != STATE_CONNECTED && connectionState != STATE_CONNECTING) { 898 Log.w(TAG, "stopVoiceRecognition: " + device + " is not connected or connecting"); 899 return false; 900 } 901 if (!mVoiceRecognitionStarted) { 902 Log.w(TAG, "stopVoiceRecognition: voice recognition was not started"); 903 return false; 904 } 905 mVoiceRecognitionStarted = false; 906 stateMachine.sendMessage(HeadsetStateMachine.VOICE_RECOGNITION_STOP, device); 907 if (!Utils.isScoManagedByAudioEnabled()) { 908 stateMachine.sendMessage(HeadsetStateMachine.DISCONNECT_AUDIO, device); 909 logScoSessionMetric( 910 device, 911 BluetoothStatsLog 912 .BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__STATE__SCO_DISCONNECT_AUDIO_END, 913 Binder.getCallingUid()); 914 } 915 } 916 917 if (Utils.isScoManagedByAudioEnabled()) { 918 // do the task outside synchronized to avoid deadlock with Audio Fwk 919 BluetoothDevice finalDevice = device; 920 mHandler.post( 921 () -> { 922 mSystemInterface.getAudioManager().clearCommunicationDevice(); 923 logScoSessionMetric( 924 finalDevice, 925 BluetoothStatsLog 926 .BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__STATE__SCO_VOICE_RECOGNITION_INITIATED_END, 927 Binder.getCallingUid()); 928 }); 929 } 930 enableSwbCodec(HeadsetHalConstants.BTHF_SWB_CODEC_VENDOR_APTX, false, device); 931 return true; 932 } 933 isAudioOn()934 boolean isAudioOn() { 935 return getNonIdleAudioDevices().size() > 0; 936 } 937 isAudioConnected(BluetoothDevice device)938 boolean isAudioConnected(BluetoothDevice device) { 939 synchronized (mStateMachines) { 940 final HeadsetStateMachine stateMachine = mStateMachines.get(device); 941 if (stateMachine == null) { 942 return false; 943 } 944 return stateMachine.getAudioState() == BluetoothHeadset.STATE_AUDIO_CONNECTED; 945 } 946 } 947 getAudioState(BluetoothDevice device)948 int getAudioState(BluetoothDevice device) { 949 synchronized (mStateMachines) { 950 final HeadsetStateMachine stateMachine = mStateMachines.get(device); 951 if (stateMachine == null) { 952 return BluetoothHeadset.STATE_AUDIO_DISCONNECTED; 953 } 954 return stateMachine.getAudioState(); 955 } 956 } 957 setAudioRouteAllowed(boolean allowed)958 public void setAudioRouteAllowed(boolean allowed) { 959 Log.i(TAG, "setAudioRouteAllowed: allowed=" + allowed + ", " + Utils.getUidPidString()); 960 mAudioRouteAllowed = allowed; 961 mNativeInterface.setScoAllowed(allowed); 962 } 963 getAudioRouteAllowed()964 public boolean getAudioRouteAllowed() { 965 return mAudioRouteAllowed; 966 } 967 setForceScoAudio(boolean forced)968 public void setForceScoAudio(boolean forced) { 969 Log.i(TAG, "setForceScoAudio: forced=" + forced + ", " + Utils.getUidPidString()); 970 mForceScoAudio = forced; 971 } 972 973 @VisibleForTesting getForceScoAudio()974 public boolean getForceScoAudio() { 975 return mForceScoAudio; 976 } 977 978 /** 979 * Get first available device for SCO audio 980 * 981 * @return first connected headset device 982 */ 983 @VisibleForTesting 984 @Nullable getFirstConnectedAudioDevice()985 public BluetoothDevice getFirstConnectedAudioDevice() { 986 ArrayList<HeadsetStateMachine> stateMachines = new ArrayList<>(); 987 synchronized (mStateMachines) { 988 List<BluetoothDevice> availableDevices = 989 getDevicesMatchingConnectionStates(CONNECTING_CONNECTED_STATES); 990 for (BluetoothDevice device : availableDevices) { 991 final HeadsetStateMachine stateMachine = mStateMachines.get(device); 992 if (stateMachine == null) { 993 continue; 994 } 995 stateMachines.add(stateMachine); 996 } 997 } 998 stateMachines.sort(Comparator.comparingLong(HeadsetStateMachine::getConnectingTimestampMs)); 999 if (stateMachines.size() > 0) { 1000 return stateMachines.get(0).getDevice(); 1001 } 1002 return null; 1003 } 1004 1005 /** 1006 * Process a change in the silence mode for a {@link BluetoothDevice}. 1007 * 1008 * @param device the device to change silence mode 1009 * @param silence true to enable silence mode, false to disable. 1010 * @return true on success, false on error 1011 */ 1012 @VisibleForTesting setSilenceMode(BluetoothDevice device, boolean silence)1013 public boolean setSilenceMode(BluetoothDevice device, boolean silence) { 1014 Log.d(TAG, "setSilenceMode(" + device + "): " + silence); 1015 1016 if (silence && Objects.equals(mActiveDevice, device)) { 1017 setActiveDevice(null); 1018 } else if (!silence && mActiveDevice == null) { 1019 // Set the device as the active device if currently no active device. 1020 setActiveDevice(device); 1021 } 1022 synchronized (mStateMachines) { 1023 final HeadsetStateMachine stateMachine = mStateMachines.get(device); 1024 if (stateMachine == null) { 1025 Log.w(TAG, "setSilenceMode: device " + device + " was never connected/connecting"); 1026 return false; 1027 } 1028 stateMachine.setSilenceDevice(silence); 1029 } 1030 1031 return true; 1032 } 1033 1034 /** 1035 * Get the Bluetooth Audio Policy stored in the state machine 1036 * 1037 * @param device the device to change silence mode 1038 * @return a {@link BluetoothSinkAudioPolicy} object 1039 */ getHfpCallAudioPolicy(BluetoothDevice device)1040 public BluetoothSinkAudioPolicy getHfpCallAudioPolicy(BluetoothDevice device) { 1041 synchronized (mStateMachines) { 1042 final HeadsetStateMachine stateMachine = mStateMachines.get(device); 1043 if (stateMachine == null) { 1044 Log.w(TAG, "getHfpCallAudioPolicy(), " + device + " does not have a state machine"); 1045 return null; 1046 } 1047 return stateMachine.getHfpCallAudioPolicy(); 1048 } 1049 } 1050 1051 /** Remove the active device */ removeActiveDevice()1052 private void removeActiveDevice() { 1053 synchronized (mStateMachines) { 1054 // As per b/202602952, if we remove the active device due to a disconnection, 1055 // we need to check if another device is connected and set it active instead. 1056 // Calling this before any other active related calls has the same effect as 1057 // a classic active device switch. 1058 BluetoothDevice fallbackDevice = getFallbackDevice(); 1059 if (fallbackDevice != null 1060 && mActiveDevice != null 1061 && getConnectionState(mActiveDevice) != STATE_CONNECTED) { 1062 setActiveDevice(fallbackDevice); 1063 return; 1064 } 1065 // Clear the active device 1066 if (mVoiceRecognitionStarted) { 1067 if (!stopVoiceRecognition(mActiveDevice)) { 1068 Log.w( 1069 TAG, 1070 "removeActiveDevice: fail to stopVoiceRecognition from " 1071 + mActiveDevice); 1072 } 1073 } 1074 if (mVirtualCallStarted) { 1075 if (!stopScoUsingVirtualVoiceCall()) { 1076 Log.w( 1077 TAG, 1078 "removeActiveDevice: fail to stopScoUsingVirtualVoiceCall from " 1079 + mActiveDevice); 1080 } 1081 } 1082 if (getAudioState(mActiveDevice) != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { 1083 int disconnectStatus = disconnectAudio(mActiveDevice); 1084 if (disconnectStatus != BluetoothStatusCodes.SUCCESS) { 1085 Log.w( 1086 TAG, 1087 "removeActiveDevice: disconnectAudio failed on " 1088 + mActiveDevice 1089 + " with status code " 1090 + disconnectStatus); 1091 } 1092 } 1093 1094 // Make sure the Audio Manager knows the previous active device is no longer active. 1095 BluetoothDevice previousActiveDevice = mActiveDevice; 1096 mActiveDevice = null; 1097 mNativeInterface.setActiveDevice(null); 1098 if (Utils.isScoManagedByAudioEnabled()) { 1099 mSystemInterface 1100 .getAudioManager() 1101 .handleBluetoothActiveDeviceChanged( 1102 null, 1103 previousActiveDevice, 1104 BluetoothProfileConnectionInfo.createHfpInfo()); 1105 } else { 1106 broadcastActiveDevice(null); 1107 } 1108 updateInbandRinging(null, true); 1109 } 1110 } 1111 1112 /** 1113 * Set the active device. 1114 * 1115 * @param device the active device 1116 * @return true on success, otherwise false 1117 */ setActiveDevice(BluetoothDevice device)1118 public boolean setActiveDevice(BluetoothDevice device) { 1119 Log.i(TAG, "setActiveDevice: device=" + device + ", " + Utils.getUidPidString()); 1120 if (device == null) { 1121 removeActiveDevice(); 1122 return true; 1123 } 1124 synchronized (mStateMachines) { 1125 if (device.equals(mActiveDevice)) { 1126 Log.i(TAG, "setActiveDevice: device " + device + " is already active"); 1127 return true; 1128 } 1129 if (getConnectionState(device) != STATE_CONNECTED) { 1130 Log.e( 1131 TAG, 1132 "setActiveDevice: Cannot set " 1133 + device 1134 + " as active, device is not connected"); 1135 return false; 1136 } 1137 if (!mNativeInterface.setActiveDevice(device)) { 1138 Log.e(TAG, "setActiveDevice: Cannot set " + device + " as active in native layer"); 1139 return false; 1140 } 1141 BluetoothDevice previousActiveDevice = mActiveDevice; 1142 mActiveDevice = device; 1143 1144 /* If HFP is getting active for a phone call and there are active LE Audio devices, 1145 * Lets inactive LeAudio device as soon as possible so there is no CISes connected 1146 * when SCO is going to be created 1147 */ 1148 if (mSystemInterface.isInCall() || mSystemInterface.isRinging()) { 1149 LeAudioService leAudioService = mFactory.getLeAudioService(); 1150 if (leAudioService != null && !leAudioService.getConnectedDevices().isEmpty()) { 1151 Log.i(TAG, "Make sure no le audio device active for HFP handover."); 1152 leAudioService.setInactiveForHfpHandover(mActiveDevice); 1153 } 1154 } 1155 1156 if (getAudioState(previousActiveDevice) != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { 1157 int disconnectStatus = disconnectAudio(previousActiveDevice); 1158 if (disconnectStatus != BluetoothStatusCodes.SUCCESS) { 1159 Log.e( 1160 TAG, 1161 "setActiveDevice: fail to disconnectAudio from " 1162 + previousActiveDevice 1163 + " with status code " 1164 + disconnectStatus); 1165 mActiveDevice = previousActiveDevice; 1166 mNativeInterface.setActiveDevice(previousActiveDevice); 1167 return false; 1168 } 1169 if (Utils.isScoManagedByAudioEnabled()) { 1170 // tell Audio Framework that active device changed 1171 mSystemInterface 1172 .getAudioManager() 1173 .handleBluetoothActiveDeviceChanged( 1174 mActiveDevice, 1175 previousActiveDevice, 1176 BluetoothProfileConnectionInfo.createHfpInfo()); 1177 } else { 1178 broadcastActiveDevice(mActiveDevice); 1179 } 1180 } else if (shouldPersistAudio()) { 1181 if (Utils.isScoManagedByAudioEnabled()) { 1182 // tell Audio Framework that active device changed 1183 mSystemInterface 1184 .getAudioManager() 1185 .handleBluetoothActiveDeviceChanged( 1186 mActiveDevice, 1187 previousActiveDevice, 1188 BluetoothProfileConnectionInfo.createHfpInfo()); 1189 // Audio Framework will handle audio transition 1190 return true; 1191 } 1192 broadcastActiveDevice(mActiveDevice); 1193 int connectStatus = connectAudio(mActiveDevice); 1194 if (connectStatus != BluetoothStatusCodes.SUCCESS) { 1195 Log.e( 1196 TAG, 1197 "setActiveDevice: fail to connectAudio to " 1198 + mActiveDevice 1199 + " with status code " 1200 + connectStatus); 1201 if (previousActiveDevice == null) { 1202 removeActiveDevice(); 1203 } else { 1204 mActiveDevice = previousActiveDevice; 1205 mNativeInterface.setActiveDevice(previousActiveDevice); 1206 } 1207 return false; 1208 } 1209 } else { 1210 if (Utils.isScoManagedByAudioEnabled()) { 1211 // tell Audio Framework that active device changed 1212 mSystemInterface 1213 .getAudioManager() 1214 .handleBluetoothActiveDeviceChanged( 1215 mActiveDevice, 1216 previousActiveDevice, 1217 BluetoothProfileConnectionInfo.createHfpInfo()); 1218 } else { 1219 broadcastActiveDevice(mActiveDevice); 1220 } 1221 } 1222 updateInbandRinging(device, true); 1223 } 1224 return true; 1225 } 1226 1227 /** 1228 * Get the active device. 1229 * 1230 * @return the active device or null if no device is active 1231 */ getActiveDevice()1232 public BluetoothDevice getActiveDevice() { 1233 synchronized (mStateMachines) { 1234 return mActiveDevice; 1235 } 1236 } 1237 connectAudio()1238 public int connectAudio() { 1239 synchronized (mStateMachines) { 1240 BluetoothDevice device = mActiveDevice; 1241 if (device == null) { 1242 Log.w(TAG, "connectAudio: no active device, " + Utils.getUidPidString()); 1243 return BluetoothStatusCodes.ERROR_NO_ACTIVE_DEVICES; 1244 } 1245 return connectAudio(device); 1246 } 1247 } 1248 connectAudio(BluetoothDevice device)1249 int connectAudio(BluetoothDevice device) { 1250 Log.i(TAG, "connectAudio: device=" + device + ", " + Utils.getUidPidString()); 1251 synchronized (mStateMachines) { 1252 final HeadsetStateMachine stateMachine = mStateMachines.get(device); 1253 if (stateMachine == null) { 1254 Log.w(TAG, "connectAudio: device " + device + " was never connected/connecting"); 1255 return BluetoothStatusCodes.ERROR_PROFILE_NOT_CONNECTED; 1256 } 1257 int scoConnectionAllowedState = isScoAcceptable(device); 1258 if (scoConnectionAllowedState != BluetoothStatusCodes.SUCCESS) { 1259 Log.w(TAG, "connectAudio, rejected SCO request to " + device); 1260 return scoConnectionAllowedState; 1261 } 1262 if (stateMachine.getConnectionState() != STATE_CONNECTED) { 1263 Log.w(TAG, "connectAudio: profile not connected"); 1264 return BluetoothStatusCodes.ERROR_PROFILE_NOT_CONNECTED; 1265 } 1266 if (stateMachine.getAudioState() != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { 1267 logD("connectAudio: audio is not idle for device " + device); 1268 logScoSessionMetric( 1269 device, 1270 BluetoothStatsLog 1271 .BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__STATE__SCO_CONNECT_AUDIO_START, 1272 Binder.getCallingUid()); 1273 return BluetoothStatusCodes.SUCCESS; 1274 } 1275 if (isAudioOn()) { 1276 Log.w( 1277 TAG, 1278 "connectAudio: audio is not idle, current audio devices are " 1279 + Arrays.toString(getNonIdleAudioDevices().toArray())); 1280 return BluetoothStatusCodes.ERROR_AUDIO_DEVICE_ALREADY_CONNECTED; 1281 } 1282 stateMachine.sendMessage(HeadsetStateMachine.CONNECT_AUDIO, device); 1283 logScoSessionMetric( 1284 device, 1285 BluetoothStatsLog 1286 .BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__STATE__SCO_CONNECT_AUDIO_START, 1287 Binder.getCallingUid()); 1288 } 1289 return BluetoothStatusCodes.SUCCESS; 1290 } 1291 getNonIdleAudioDevices()1292 private List<BluetoothDevice> getNonIdleAudioDevices() { 1293 ArrayList<BluetoothDevice> devices = new ArrayList<>(); 1294 synchronized (mStateMachines) { 1295 for (HeadsetStateMachine stateMachine : mStateMachines.values()) { 1296 if (stateMachine.getAudioState() != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { 1297 devices.add(stateMachine.getDevice()); 1298 } 1299 } 1300 } 1301 return devices; 1302 } 1303 disconnectAudio()1304 int disconnectAudio() { 1305 int disconnectResult = BluetoothStatusCodes.ERROR_NO_ACTIVE_DEVICES; 1306 synchronized (mStateMachines) { 1307 for (BluetoothDevice device : getNonIdleAudioDevices()) { 1308 disconnectResult = disconnectAudio(device); 1309 if (disconnectResult == BluetoothStatusCodes.SUCCESS) { 1310 logScoSessionMetric( 1311 device, 1312 BluetoothStatsLog 1313 .BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__STATE__SCO_DISCONNECT_AUDIO_END, 1314 Binder.getCallingUid()); 1315 return disconnectResult; 1316 } else { 1317 Log.e( 1318 TAG, 1319 "disconnectAudio() from " 1320 + device 1321 + " failed with status code " 1322 + disconnectResult); 1323 } 1324 } 1325 } 1326 logD("disconnectAudio() no active audio connection"); 1327 return disconnectResult; 1328 } 1329 disconnectAudio(BluetoothDevice device)1330 int disconnectAudio(BluetoothDevice device) { 1331 synchronized (mStateMachines) { 1332 Log.i(TAG, "disconnectAudio: device=" + device + ", " + Utils.getUidPidString()); 1333 final HeadsetStateMachine stateMachine = mStateMachines.get(device); 1334 if (stateMachine == null) { 1335 Log.w(TAG, "disconnectAudio: device " + device + " was never connected/connecting"); 1336 return BluetoothStatusCodes.ERROR_PROFILE_NOT_CONNECTED; 1337 } 1338 if (stateMachine.getAudioState() == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { 1339 Log.w(TAG, "disconnectAudio, audio is already disconnected for " + device); 1340 return BluetoothStatusCodes.ERROR_AUDIO_DEVICE_ALREADY_DISCONNECTED; 1341 } 1342 stateMachine.sendMessage(HeadsetStateMachine.DISCONNECT_AUDIO, device); 1343 } 1344 return BluetoothStatusCodes.SUCCESS; 1345 } 1346 isVirtualCallStarted()1347 boolean isVirtualCallStarted() { 1348 synchronized (mStateMachines) { 1349 return mVirtualCallStarted; 1350 } 1351 } 1352 startScoUsingVirtualVoiceCall()1353 boolean startScoUsingVirtualVoiceCall() { 1354 Log.i(TAG, "startScoUsingVirtualVoiceCall: " + Utils.getUidPidString()); 1355 synchronized (mStateMachines) { 1356 // TODO(b/79660380): Workaround in case voice recognition was not terminated properly 1357 if (mVoiceRecognitionStarted) { 1358 boolean status = stopVoiceRecognition(mActiveDevice); 1359 Log.w( 1360 TAG, 1361 "startScoUsingVirtualVoiceCall: voice recognition is still active, " 1362 + "just called stopVoiceRecognition, returned " 1363 + status 1364 + " on " 1365 + mActiveDevice 1366 + ", please try again"); 1367 mVoiceRecognitionStarted = false; 1368 return false; 1369 } 1370 if (!isAudioModeIdle()) { 1371 Log.w( 1372 TAG, 1373 "startScoUsingVirtualVoiceCall: audio mode not idle, active device is " 1374 + mActiveDevice); 1375 return false; 1376 } 1377 // Audio should not be on when no audio mode is active 1378 if (isAudioOn()) { 1379 // Disconnect audio so that API user can try later 1380 int status = disconnectAudio(); 1381 Log.w( 1382 TAG, 1383 "startScoUsingVirtualVoiceCall: audio is still active, please wait for " 1384 + "audio to be disconnected, disconnectAudio() returned " 1385 + status 1386 + ", active device is " 1387 + mActiveDevice); 1388 return false; 1389 } 1390 if (mActiveDevice == null) { 1391 Log.w(TAG, "startScoUsingVirtualVoiceCall: no active device"); 1392 return false; 1393 } 1394 if (SystemProperties.getBoolean(REJECT_SCO_IF_HFPC_CONNECTED_PROPERTY, false) 1395 && isHeadsetClientConnected()) { 1396 Log.w(TAG, "startScoUsingVirtualVoiceCall: rejected SCO since HFPC is connected!"); 1397 return false; 1398 } 1399 mVirtualCallStarted = true; 1400 // Send virtual phone state changed to initialize SCO 1401 phoneStateChanged(0, 0, HeadsetHalConstants.CALL_STATE_DIALING, "", 0, "", true); 1402 phoneStateChanged(0, 0, HeadsetHalConstants.CALL_STATE_ALERTING, "", 0, "", true); 1403 phoneStateChanged(1, 0, HeadsetHalConstants.CALL_STATE_IDLE, "", 0, "", true); 1404 logScoSessionMetric( 1405 mActiveDevice, 1406 BluetoothStatsLog 1407 .BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__STATE__SCO_VIRTUAL_VOICE_INITIATED_START, 1408 Binder.getCallingUid()); 1409 return true; 1410 } 1411 } 1412 stopScoUsingVirtualVoiceCall()1413 boolean stopScoUsingVirtualVoiceCall() { 1414 Log.i(TAG, "stopScoUsingVirtualVoiceCall: " + Utils.getUidPidString()); 1415 synchronized (mStateMachines) { 1416 // 1. Check if virtual call has already started 1417 if (!mVirtualCallStarted) { 1418 Log.w(TAG, "stopScoUsingVirtualVoiceCall: virtual call not started"); 1419 return false; 1420 } 1421 mVirtualCallStarted = false; 1422 // 2. Send virtual phone state changed to close SCO 1423 phoneStateChanged(0, 0, HeadsetHalConstants.CALL_STATE_IDLE, "", 0, "", true); 1424 } 1425 logScoSessionMetric( 1426 mActiveDevice, 1427 BluetoothStatsLog 1428 .BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__STATE__SCO_VIRTUAL_VOICE_INITIATED_END, 1429 Binder.getCallingUid()); 1430 return true; 1431 } 1432 1433 class DialingOutTimeoutEvent implements Runnable { 1434 BluetoothDevice mDialingOutDevice; 1435 DialingOutTimeoutEvent(BluetoothDevice fromDevice)1436 DialingOutTimeoutEvent(BluetoothDevice fromDevice) { 1437 mDialingOutDevice = fromDevice; 1438 } 1439 1440 @Override run()1441 public void run() { 1442 synchronized (mStateMachines) { 1443 mDialingOutTimeoutEvent = null; 1444 doForStateMachine( 1445 mDialingOutDevice, 1446 stateMachine -> 1447 stateMachine.sendMessage( 1448 HeadsetStateMachine.DIALING_OUT_RESULT, 1449 0 /* fail */, 1450 0, 1451 mDialingOutDevice)); 1452 } 1453 } 1454 1455 @Override toString()1456 public String toString() { 1457 return "DialingOutTimeoutEvent[" + mDialingOutDevice + "]"; 1458 } 1459 } 1460 1461 /** 1462 * Dial an outgoing call as requested by the remote device 1463 * 1464 * @param fromDevice remote device that initiated this dial out action 1465 * @param dialNumber number to dial 1466 * @return true on successful dial out 1467 */ 1468 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) dialOutgoingCall(BluetoothDevice fromDevice, String dialNumber)1469 public boolean dialOutgoingCall(BluetoothDevice fromDevice, String dialNumber) { 1470 synchronized (mStateMachines) { 1471 Log.i(TAG, "dialOutgoingCall: from " + fromDevice); 1472 if (mDialingOutTimeoutEvent != null) { 1473 Log.e(TAG, "dialOutgoingCall, already dialing by " + mDialingOutTimeoutEvent); 1474 return false; 1475 } 1476 if (isVirtualCallStarted()) { 1477 if (!stopScoUsingVirtualVoiceCall()) { 1478 Log.e(TAG, "dialOutgoingCall failed to stop current virtual call"); 1479 return false; 1480 } 1481 } 1482 if (!setActiveDevice(fromDevice)) { 1483 Log.e(TAG, "dialOutgoingCall failed to set active device to " + fromDevice); 1484 return false; 1485 } 1486 Intent intent = 1487 new Intent( 1488 Intent.ACTION_CALL_PRIVILEGED, 1489 Uri.fromParts(PhoneAccount.SCHEME_TEL, dialNumber, null)); 1490 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 1491 startActivity(intent); 1492 mDialingOutTimeoutEvent = new DialingOutTimeoutEvent(fromDevice); 1493 mStateMachinesThreadHandler.postDelayed( 1494 mDialingOutTimeoutEvent, DIALING_OUT_TIMEOUT_MS); 1495 return true; 1496 } 1497 } 1498 1499 /** 1500 * Check if any connected headset has started dialing calls 1501 * 1502 * @return true if some device has started dialing calls 1503 */ 1504 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) hasDeviceInitiatedDialingOut()1505 public boolean hasDeviceInitiatedDialingOut() { 1506 synchronized (mStateMachines) { 1507 return mDialingOutTimeoutEvent != null; 1508 } 1509 } 1510 1511 class VoiceRecognitionTimeoutEvent implements Runnable { 1512 BluetoothDevice mVoiceRecognitionDevice; 1513 VoiceRecognitionTimeoutEvent(BluetoothDevice device)1514 VoiceRecognitionTimeoutEvent(BluetoothDevice device) { 1515 mVoiceRecognitionDevice = device; 1516 } 1517 1518 @Override run()1519 public void run() { 1520 synchronized (mStateMachines) { 1521 if (mSystemInterface.getVoiceRecognitionWakeLock().isHeld()) { 1522 try { 1523 mSystemInterface.getVoiceRecognitionWakeLock().release(); 1524 } catch (RuntimeException e) { 1525 Log.d(TAG, "non properly release getVoiceRecognitionWakeLock", e); 1526 } 1527 } 1528 mVoiceRecognitionTimeoutEvent = null; 1529 doForStateMachine( 1530 mVoiceRecognitionDevice, 1531 stateMachine -> 1532 stateMachine.sendMessage( 1533 HeadsetStateMachine.VOICE_RECOGNITION_RESULT, 1534 0 /* fail */, 1535 0, 1536 mVoiceRecognitionDevice)); 1537 logScoSessionMetric( 1538 mActiveDevice, 1539 BluetoothStatsLog 1540 .BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__STATE__SCO_VOICE_RECOGNITION_HEADSET_TIMEOUT, 1541 Binder.getCallingUid()); 1542 } 1543 } 1544 1545 @Override toString()1546 public String toString() { 1547 return "VoiceRecognitionTimeoutEvent[" + mVoiceRecognitionDevice + "]"; 1548 } 1549 } 1550 startVoiceRecognitionByHeadset(BluetoothDevice fromDevice)1551 boolean startVoiceRecognitionByHeadset(BluetoothDevice fromDevice) { 1552 synchronized (mStateMachines) { 1553 Log.i(TAG, "startVoiceRecognitionByHeadset: from " + fromDevice); 1554 // TODO(b/79660380): Workaround in case voice recognition was not terminated properly 1555 if (mVoiceRecognitionStarted) { 1556 boolean status = stopVoiceRecognition(mActiveDevice); 1557 Log.w( 1558 TAG, 1559 "startVoiceRecognitionByHeadset: voice recognition is still active, " 1560 + "just called stopVoiceRecognition, returned " 1561 + status 1562 + " on " 1563 + mActiveDevice 1564 + ", please try again"); 1565 mVoiceRecognitionStarted = false; 1566 return false; 1567 } 1568 if (fromDevice == null) { 1569 Log.e(TAG, "startVoiceRecognitionByHeadset: fromDevice is null"); 1570 return false; 1571 } 1572 if (!isAudioModeIdle()) { 1573 Log.w( 1574 TAG, 1575 "startVoiceRecognitionByHeadset: audio mode not idle, active device is " 1576 + mActiveDevice); 1577 return false; 1578 } 1579 // Audio should not be on when no audio mode is active 1580 if (isAudioOn()) { 1581 // Disconnect audio so that user can try later 1582 int status = disconnectAudio(); 1583 Log.w( 1584 TAG, 1585 "startVoiceRecognitionByHeadset: audio is still active, please wait for" 1586 + " audio to be disconnected, disconnectAudio() returned " 1587 + status 1588 + ", active device is " 1589 + mActiveDevice); 1590 return false; 1591 } 1592 // Do not start new request until the current one is finished or timeout 1593 if (mVoiceRecognitionTimeoutEvent != null) { 1594 Log.w( 1595 TAG, 1596 "startVoiceRecognitionByHeadset: failed request from " 1597 + fromDevice 1598 + ", already pending by " 1599 + mVoiceRecognitionTimeoutEvent); 1600 return false; 1601 } 1602 if (!setActiveDevice(fromDevice)) { 1603 Log.w( 1604 TAG, 1605 "startVoiceRecognitionByHeadset: failed to set " 1606 + fromDevice 1607 + " as active"); 1608 return false; 1609 } 1610 if (!mSystemInterface.activateVoiceRecognition()) { 1611 Log.w(TAG, "startVoiceRecognitionByHeadset: failed request from " + fromDevice); 1612 return false; 1613 } 1614 if (SystemProperties.getBoolean(REJECT_SCO_IF_HFPC_CONNECTED_PROPERTY, false) 1615 && isHeadsetClientConnected()) { 1616 Log.w(TAG, "startVoiceRecognitionByHeadset: rejected SCO since HFPC is connected!"); 1617 return false; 1618 } 1619 mVoiceRecognitionTimeoutEvent = new VoiceRecognitionTimeoutEvent(fromDevice); 1620 mStateMachinesThreadHandler.postDelayed( 1621 mVoiceRecognitionTimeoutEvent, sStartVrTimeoutMs); 1622 1623 if (!mSystemInterface.getVoiceRecognitionWakeLock().isHeld()) { 1624 mSystemInterface.getVoiceRecognitionWakeLock().acquire(sStartVrTimeoutMs); 1625 } 1626 enableSwbCodec(HeadsetHalConstants.BTHF_SWB_CODEC_VENDOR_APTX, true, fromDevice); 1627 logScoSessionMetric( 1628 mActiveDevice, 1629 BluetoothStatsLog 1630 .BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__STATE__SCO_VOICE_RECOGNITION_HEADSET_START, 1631 Binder.getCallingUid()); 1632 return true; 1633 } 1634 } 1635 stopVoiceRecognitionByHeadset(BluetoothDevice fromDevice)1636 boolean stopVoiceRecognitionByHeadset(BluetoothDevice fromDevice) { 1637 synchronized (mStateMachines) { 1638 Log.i(TAG, "stopVoiceRecognitionByHeadset: from " + fromDevice); 1639 if (!Objects.equals(fromDevice, mActiveDevice)) { 1640 Log.w( 1641 TAG, 1642 "stopVoiceRecognitionByHeadset: " 1643 + fromDevice 1644 + " is not active, active device is " 1645 + mActiveDevice); 1646 return false; 1647 } 1648 if (!mVoiceRecognitionStarted && mVoiceRecognitionTimeoutEvent == null) { 1649 Log.w( 1650 TAG, 1651 "stopVoiceRecognitionByHeadset: voice recognition not started, device=" 1652 + fromDevice); 1653 return false; 1654 } 1655 if (mVoiceRecognitionTimeoutEvent != null) { 1656 if (mSystemInterface.getVoiceRecognitionWakeLock().isHeld()) { 1657 try { 1658 mSystemInterface.getVoiceRecognitionWakeLock().release(); 1659 } catch (RuntimeException e) { 1660 Log.d(TAG, "non properly release getVoiceRecognitionWakeLock", e); 1661 } 1662 } 1663 mStateMachinesThreadHandler.removeCallbacks(mVoiceRecognitionTimeoutEvent); 1664 1665 mVoiceRecognitionTimeoutEvent = null; 1666 } 1667 if (mVoiceRecognitionStarted) { 1668 int disconnectStatus = disconnectAudio(); 1669 if (disconnectStatus != BluetoothStatusCodes.SUCCESS) { 1670 Log.w( 1671 TAG, 1672 "stopVoiceRecognitionByHeadset: failed to disconnect audio from " 1673 + fromDevice 1674 + " with status code " 1675 + disconnectStatus); 1676 } 1677 mVoiceRecognitionStarted = false; 1678 } 1679 if (!mSystemInterface.deactivateVoiceRecognition()) { 1680 Log.w(TAG, "stopVoiceRecognitionByHeadset: failed request from " + fromDevice); 1681 return false; 1682 } 1683 enableSwbCodec(HeadsetHalConstants.BTHF_SWB_CODEC_VENDOR_APTX, false, fromDevice); 1684 logScoSessionMetric( 1685 mActiveDevice, 1686 BluetoothStatsLog 1687 .BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__STATE__SCO_VOICE_RECOGNITION_HEADSET_END, 1688 Binder.getCallingUid()); 1689 return true; 1690 } 1691 } 1692 phoneStateChanged( int numActive, int numHeld, int callState, String number, int type, String name, boolean isVirtualCall)1693 public void phoneStateChanged( 1694 int numActive, 1695 int numHeld, 1696 int callState, 1697 String number, 1698 int type, 1699 String name, 1700 boolean isVirtualCall) { 1701 synchronized (mStateMachines) { 1702 // Should stop all other audio mode in this case 1703 if ((numActive + numHeld) > 0 || callState != HeadsetHalConstants.CALL_STATE_IDLE) { 1704 if (!isVirtualCall && mVirtualCallStarted) { 1705 // stop virtual voice call if there is an incoming Telecom call update 1706 stopScoUsingVirtualVoiceCall(); 1707 } 1708 if (mVoiceRecognitionStarted) { 1709 // stop voice recognition if there is any incoming call 1710 stopVoiceRecognition(mActiveDevice); 1711 } 1712 } 1713 if (mDialingOutTimeoutEvent != null) { 1714 // Send result to state machine when dialing starts 1715 if (callState == HeadsetHalConstants.CALL_STATE_DIALING) { 1716 mStateMachinesThreadHandler.removeCallbacks(mDialingOutTimeoutEvent); 1717 doForStateMachine( 1718 mDialingOutTimeoutEvent.mDialingOutDevice, 1719 stateMachine -> 1720 stateMachine.sendMessage( 1721 HeadsetStateMachine.DIALING_OUT_RESULT, 1722 1 /* success */, 1723 0, 1724 mDialingOutTimeoutEvent.mDialingOutDevice)); 1725 } else if (callState == HeadsetHalConstants.CALL_STATE_ACTIVE 1726 || callState == HeadsetHalConstants.CALL_STATE_IDLE) { 1727 // Clear the timeout event when the call is connected or disconnected 1728 if (!mStateMachinesThreadHandler.hasCallbacks(mDialingOutTimeoutEvent)) { 1729 mDialingOutTimeoutEvent = null; 1730 } 1731 } 1732 } 1733 } 1734 mStateMachinesThreadHandler.post( 1735 () -> { 1736 boolean isCallIdleBefore = mSystemInterface.isCallIdle(); 1737 mSystemInterface.getHeadsetPhoneState().setNumActiveCall(numActive); 1738 mSystemInterface.getHeadsetPhoneState().setNumHeldCall(numHeld); 1739 mSystemInterface.getHeadsetPhoneState().setCallState(callState); 1740 logScoSessionMetric( 1741 mActiveDevice, 1742 BluetoothStatsLog 1743 .BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__STATE__SCO_TELECOM_INITIATED_START, 1744 Binder.getCallingUid()); 1745 // Suspend A2DP when call about is about to become active 1746 if (mActiveDevice != null 1747 && callState != HeadsetHalConstants.CALL_STATE_DISCONNECTED 1748 && !mSystemInterface.isCallIdle() 1749 && isCallIdleBefore 1750 && !Utils.isScoManagedByAudioEnabled()) { 1751 mSystemInterface.getAudioManager().setA2dpSuspended(true); 1752 if (isAtLeastU()) { 1753 mSystemInterface.getAudioManager().setLeAudioSuspended(true); 1754 } 1755 } 1756 }); 1757 doForEachConnectedStateMachine( 1758 stateMachine -> 1759 stateMachine.sendMessage( 1760 HeadsetStateMachine.CALL_STATE_CHANGED, 1761 new HeadsetCallState( 1762 numActive, numHeld, callState, number, type, name))); 1763 mStateMachinesThreadHandler.post( 1764 () -> { 1765 if (callState == HeadsetHalConstants.CALL_STATE_IDLE 1766 && mSystemInterface.isCallIdle() 1767 && !isAudioOn() 1768 && !Utils.isScoManagedByAudioEnabled()) { 1769 // Resume A2DP when call ended and SCO is not connected 1770 mSystemInterface.getAudioManager().setA2dpSuspended(false); 1771 if (isAtLeastU()) { 1772 mSystemInterface.getAudioManager().setLeAudioSuspended(false); 1773 } 1774 } 1775 }); 1776 if (callState == HeadsetHalConstants.CALL_STATE_IDLE) { 1777 final HeadsetStateMachine stateMachine = mStateMachines.get(mActiveDevice); 1778 if (stateMachine == null) { 1779 Log.d(TAG, "phoneStateChanged: CALL_STATE_IDLE, mActiveDevice is Null"); 1780 } else { 1781 BluetoothSinkAudioPolicy currentPolicy = stateMachine.getHfpCallAudioPolicy(); 1782 if (currentPolicy != null 1783 && currentPolicy.getActiveDevicePolicyAfterConnection() 1784 == BluetoothSinkAudioPolicy.POLICY_NOT_ALLOWED) { 1785 /* 1786 * If the active device was set because of the pick up audio policy and the 1787 * connecting policy is NOT_ALLOWED, then after the call is terminated, we must 1788 * de-activate this device. If there is a fallback mechanism, we should follow 1789 * it to set fallback device be active. 1790 */ 1791 removeActiveDevice(); 1792 1793 BluetoothDevice fallbackDevice = getFallbackDevice(); 1794 if (fallbackDevice != null 1795 && getConnectionState(fallbackDevice) == STATE_CONNECTED) { 1796 Log.d( 1797 TAG, 1798 "BluetoothSinkAudioPolicy set fallbackDevice=" 1799 + fallbackDevice 1800 + " active"); 1801 setActiveDevice(fallbackDevice); 1802 } 1803 } 1804 } 1805 logScoSessionMetric( 1806 mActiveDevice, 1807 BluetoothStatsLog 1808 .BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__STATE__SCO_TELECOM_INITIATED_END, 1809 Binder.getCallingUid()); 1810 } 1811 } 1812 clccResponse( int index, int direction, int status, int mode, boolean mpty, String number, int type)1813 public void clccResponse( 1814 int index, int direction, int status, int mode, boolean mpty, String number, int type) { 1815 mPendingClccResponses.add( 1816 stateMachine -> 1817 stateMachine.sendMessage( 1818 HeadsetStateMachine.SEND_CLCC_RESPONSE, 1819 new HeadsetClccResponse( 1820 index, direction, status, mode, mpty, number, type))); 1821 if (index == CLCC_END_MARK_INDEX) { 1822 doForEachConnectedStateMachine(mPendingClccResponses); 1823 mPendingClccResponses.clear(); 1824 } 1825 } 1826 sendVendorSpecificResultCode(BluetoothDevice device, String command, String arg)1827 boolean sendVendorSpecificResultCode(BluetoothDevice device, String command, String arg) { 1828 synchronized (mStateMachines) { 1829 final HeadsetStateMachine stateMachine = mStateMachines.get(device); 1830 if (stateMachine == null) { 1831 Log.w( 1832 TAG, 1833 "sendVendorSpecificResultCode: device " 1834 + device 1835 + " was never connected/connecting"); 1836 return false; 1837 } 1838 int connectionState = stateMachine.getConnectionState(); 1839 if (connectionState != STATE_CONNECTED) { 1840 return false; 1841 } 1842 // Currently we support only "+ANDROID". 1843 if (!command.equals(BluetoothHeadset.VENDOR_RESULT_CODE_COMMAND_ANDROID)) { 1844 Log.w(TAG, "Disallowed unsolicited result code command: " + command); 1845 return false; 1846 } 1847 stateMachine.sendMessage( 1848 HeadsetStateMachine.SEND_VENDOR_SPECIFIC_RESULT_CODE, 1849 new HeadsetVendorSpecificResultCode(device, command, arg)); 1850 } 1851 return true; 1852 } 1853 1854 /** 1855 * Checks if headset devices are able to get inband ringing. 1856 * 1857 * @return True if inband ringing is enabled. 1858 */ isInbandRingingEnabled()1859 public boolean isInbandRingingEnabled() { 1860 boolean isInbandRingingSupported = 1861 getResources() 1862 .getBoolean( 1863 com.android.bluetooth.R.bool 1864 .config_bluetooth_hfp_inband_ringing_support); 1865 1866 boolean inbandRingtoneAllowedByPolicy = true; 1867 List<BluetoothDevice> audioConnectableDevices = getConnectedDevices(); 1868 if (audioConnectableDevices.size() == 1) { 1869 BluetoothDevice connectedDevice = audioConnectableDevices.get(0); 1870 BluetoothSinkAudioPolicy callAudioPolicy = getHfpCallAudioPolicy(connectedDevice); 1871 if (callAudioPolicy != null 1872 && callAudioPolicy.getInBandRingtonePolicy() 1873 == BluetoothSinkAudioPolicy.POLICY_NOT_ALLOWED) { 1874 inbandRingtoneAllowedByPolicy = false; 1875 } 1876 } 1877 1878 return isInbandRingingSupported 1879 && !SystemProperties.getBoolean(DISABLE_INBAND_RINGING_PROPERTY, false) 1880 && !mInbandRingingRuntimeDisable 1881 && inbandRingtoneAllowedByPolicy 1882 && !isHeadsetClientConnected(); 1883 } 1884 isHeadsetClientConnected()1885 private static boolean isHeadsetClientConnected() { 1886 HeadsetClientService headsetClientService = HeadsetClientService.getHeadsetClientService(); 1887 if (headsetClientService == null) { 1888 return false; 1889 } 1890 return !(headsetClientService.getConnectedDevices().isEmpty()); 1891 } 1892 1893 /** 1894 * Called from {@link HeadsetStateMachine} in state machine thread when there is a connection 1895 * state change 1896 * 1897 * @param device remote device 1898 * @param fromState from which connection state is the change 1899 * @param toState to which connection state is the change 1900 */ 1901 @VisibleForTesting onConnectionStateChangedFromStateMachine( BluetoothDevice device, int fromState, int toState)1902 public void onConnectionStateChangedFromStateMachine( 1903 BluetoothDevice device, int fromState, int toState) { 1904 if (fromState != STATE_CONNECTED && toState == STATE_CONNECTED) { 1905 updateInbandRinging(device, true); 1906 } 1907 if (fromState != STATE_DISCONNECTED && toState == STATE_DISCONNECTED) { 1908 updateInbandRinging(device, false); 1909 if (device.equals(mActiveDevice)) { 1910 setActiveDevice(null); 1911 } 1912 } 1913 1914 mAdapterService 1915 .getActiveDeviceManager() 1916 .profileConnectionStateChanged( 1917 BluetoothProfile.HEADSET, device, fromState, toState); 1918 mAdapterService 1919 .getSilenceDeviceManager() 1920 .hfpConnectionStateChanged(device, fromState, toState); 1921 mAdapterService 1922 .getRemoteDevices() 1923 .handleHeadsetConnectionStateChanged(device, fromState, toState); 1924 mAdapterService.notifyProfileConnectionStateChangeToGatt( 1925 BluetoothProfile.HEADSET, fromState, toState); 1926 mAdapterService.handleProfileConnectionStateChange( 1927 BluetoothProfile.HEADSET, device, fromState, toState); 1928 mAdapterService.updateProfileConnectionAdapterProperties( 1929 device, BluetoothProfile.HEADSET, toState, fromState); 1930 } 1931 1932 /** Called from {@link HeadsetClientStateMachine} to update inband ringing status. */ updateInbandRinging(BluetoothDevice device, boolean connected)1933 public void updateInbandRinging(BluetoothDevice device, boolean connected) { 1934 synchronized (mStateMachines) { 1935 final boolean inbandRingingRuntimeDisable = mInbandRingingRuntimeDisable; 1936 1937 if (getConnectedDevices().size() > 1 1938 || isHeadsetClientConnected() 1939 || mActiveDevice == null) { 1940 mInbandRingingRuntimeDisable = true; 1941 } else { 1942 mInbandRingingRuntimeDisable = false; 1943 } 1944 1945 final boolean updateAll = inbandRingingRuntimeDisable != mInbandRingingRuntimeDisable; 1946 1947 Log.i( 1948 TAG, 1949 "updateInbandRinging():" 1950 + " Device=" 1951 + device 1952 + " ActiveDevice=" 1953 + mActiveDevice 1954 + " enabled=" 1955 + !mInbandRingingRuntimeDisable 1956 + " connected=" 1957 + connected 1958 + " Update all=" 1959 + updateAll); 1960 1961 StateMachineTask sendBsirTask = 1962 stateMachine -> 1963 stateMachine.sendMessage( 1964 HeadsetStateMachine.SEND_BSIR, 1965 mInbandRingingRuntimeDisable ? 0 : 1); 1966 1967 if (updateAll) { 1968 doForEachConnectedStateMachine(sendBsirTask); 1969 } else if (connected) { 1970 // Same Inband ringing status, send +BSIR only to the new connected device 1971 doForStateMachine(device, sendBsirTask); 1972 } 1973 } 1974 } 1975 1976 /** 1977 * Check if no audio mode is active 1978 * 1979 * @return false if virtual call, voice recognition, or Telecom call is active, true if all idle 1980 */ isAudioModeIdle()1981 private boolean isAudioModeIdle() { 1982 synchronized (mStateMachines) { 1983 if (mVoiceRecognitionStarted || mVirtualCallStarted || !mSystemInterface.isCallIdle()) { 1984 Log.i( 1985 TAG, 1986 "isAudioModeIdle: not idle, mVoiceRecognitionStarted=" 1987 + mVoiceRecognitionStarted 1988 + ", mVirtualCallStarted=" 1989 + mVirtualCallStarted 1990 + ", isCallIdle=" 1991 + mSystemInterface.isCallIdle()); 1992 return false; 1993 } 1994 return true; 1995 } 1996 } 1997 1998 /** 1999 * Check if the device only allows HFP profile as audio profile 2000 * 2001 * @param device Bluetooth device 2002 * @return true if it is a BluetoothDevice with only HFP profile connectable 2003 */ isHFPAudioOnly(@onNull BluetoothDevice device)2004 private boolean isHFPAudioOnly(@NonNull BluetoothDevice device) { 2005 int hfpPolicy = 2006 mDatabaseManager.getProfileConnectionPolicy(device, BluetoothProfile.HEADSET); 2007 int a2dpPolicy = mDatabaseManager.getProfileConnectionPolicy(device, BluetoothProfile.A2DP); 2008 int leAudioPolicy = 2009 mDatabaseManager.getProfileConnectionPolicy(device, BluetoothProfile.LE_AUDIO); 2010 int ashaPolicy = 2011 mDatabaseManager.getProfileConnectionPolicy(device, BluetoothProfile.HEARING_AID); 2012 return hfpPolicy == CONNECTION_POLICY_ALLOWED 2013 && a2dpPolicy != CONNECTION_POLICY_ALLOWED 2014 && leAudioPolicy != CONNECTION_POLICY_ALLOWED 2015 && ashaPolicy != CONNECTION_POLICY_ALLOWED; 2016 } 2017 shouldCallAudioBeActive()2018 private boolean shouldCallAudioBeActive() { 2019 return mSystemInterface.isInCall() 2020 || (mSystemInterface.isRinging() && isInbandRingingEnabled()); 2021 } 2022 2023 /** 2024 * Only persist audio during active device switch when call audio is supposed to be active and 2025 * virtual call has not been started. Virtual call is ignored because AudioService and 2026 * applications should reconnect SCO during active device switch and forcing SCO connection here 2027 * will make AudioService think SCO is started externally instead of by one of its SCO clients. 2028 * 2029 * @return true if call audio should be active and no virtual call is going on 2030 */ shouldPersistAudio()2031 private boolean shouldPersistAudio() { 2032 return !mVirtualCallStarted && shouldCallAudioBeActive(); 2033 } 2034 2035 /** 2036 * Called from {@link HeadsetStateMachine} in state machine thread when there is a audio 2037 * connection state change 2038 * 2039 * @param device remote device 2040 * @param fromState from which audio connection state is the change 2041 * @param toState to which audio connection state is the change 2042 */ 2043 @VisibleForTesting onAudioStateChangedFromStateMachine( BluetoothDevice device, int fromState, int toState)2044 public void onAudioStateChangedFromStateMachine( 2045 BluetoothDevice device, int fromState, int toState) { 2046 synchronized (mStateMachines) { 2047 if (toState == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { 2048 if (fromState != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { 2049 if (mActiveDevice != null 2050 && !mActiveDevice.equals(device) 2051 && shouldPersistAudio()) { 2052 int connectStatus = connectAudio(mActiveDevice); 2053 if (connectStatus != BluetoothStatusCodes.SUCCESS) { 2054 Log.w( 2055 TAG, 2056 "onAudioStateChangedFromStateMachine, failed to connect" 2057 + " audio to new " 2058 + "active device " 2059 + mActiveDevice 2060 + ", after " 2061 + device 2062 + " is disconnected from SCO due to" 2063 + " status code " 2064 + connectStatus); 2065 } 2066 } 2067 } 2068 if (mVoiceRecognitionStarted) { 2069 if (!stopVoiceRecognitionByHeadset(device)) { 2070 Log.w( 2071 TAG, 2072 "onAudioStateChangedFromStateMachine: failed to stop voice " 2073 + "recognition"); 2074 } 2075 } 2076 if (mVirtualCallStarted) { 2077 if (!stopScoUsingVirtualVoiceCall()) { 2078 Log.w( 2079 TAG, 2080 "onAudioStateChangedFromStateMachine: failed to stop virtual " 2081 + "voice call"); 2082 } 2083 } 2084 // Resumes LE audio previous active device if HFP handover happened before. 2085 // Do it here because some controllers cannot handle SCO and CIS 2086 // co-existence see {@link LeAudioService#setInactiveForHfpHandover} 2087 LeAudioService leAudioService = mFactory.getLeAudioService(); 2088 boolean isLeAudioConnectedDeviceNotActive = 2089 leAudioService != null 2090 && !leAudioService.getConnectedDevices().isEmpty() 2091 && leAudioService.getActiveDevices().get(0) == null; 2092 // usually controller limitation cause CONNECTING -> DISCONNECTED, so only 2093 // resume LE audio active device if it is HFP audio only and SCO disconnected 2094 if (fromState != BluetoothHeadset.STATE_AUDIO_CONNECTING 2095 && isHFPAudioOnly(device) 2096 && isLeAudioConnectedDeviceNotActive) { 2097 leAudioService.setActiveAfterHfpHandover(); 2098 } 2099 2100 // Unsuspend A2DP when SCO connection is gone and call state is idle 2101 if (mSystemInterface.isCallIdle() && !Utils.isScoManagedByAudioEnabled()) { 2102 mSystemInterface.getAudioManager().setA2dpSuspended(false); 2103 if (isAtLeastU()) { 2104 mSystemInterface.getAudioManager().setLeAudioSuspended(false); 2105 } 2106 } 2107 } 2108 } 2109 } 2110 broadcastActiveDevice(BluetoothDevice device)2111 private void broadcastActiveDevice(BluetoothDevice device) { 2112 logD("broadcastActiveDevice: " + device); 2113 2114 mAdapterService.handleActiveDeviceChange(BluetoothProfile.HEADSET, device); 2115 2116 BluetoothStatsLog.write( 2117 BluetoothStatsLog.BLUETOOTH_ACTIVE_DEVICE_CHANGED, 2118 BluetoothProfile.HEADSET, 2119 mAdapterService.obfuscateAddress(device), 2120 mAdapterService.getMetricId(device)); 2121 2122 Intent intent = new Intent(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED); 2123 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 2124 intent.addFlags( 2125 Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT 2126 | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); 2127 sendBroadcastAsUser( 2128 intent, 2129 UserHandle.ALL, 2130 BLUETOOTH_CONNECT, 2131 Utils.getTempBroadcastOptions().toBundle()); 2132 } 2133 2134 /* Notifications of audio device connection/disconnection events. */ 2135 private class AudioManagerAudioDeviceCallback extends AudioDeviceCallback { 2136 @Override onAudioDevicesAdded(AudioDeviceInfo[] addedDevices)2137 public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) { 2138 if (mSystemInterface.getAudioManager() == null || mAdapterService == null) { 2139 Log.e(TAG, "Callback called when A2dpService is stopped"); 2140 return; 2141 } 2142 2143 synchronized (mStateMachines) { 2144 for (AudioDeviceInfo deviceInfo : addedDevices) { 2145 if (deviceInfo.getType() != AudioDeviceInfo.TYPE_BLUETOOTH_SCO) { 2146 continue; 2147 } 2148 2149 String address = deviceInfo.getAddress(); 2150 if (address.equals("00:00:00:00:00:00")) { 2151 continue; 2152 } 2153 2154 byte[] addressBytes = Utils.getBytesFromAddress(address); 2155 BluetoothDevice device = mAdapterService.getDeviceFromByte(addressBytes); 2156 2157 Log.d( 2158 TAG, 2159 " onAudioDevicesAdded: " 2160 + device 2161 + ", device type: " 2162 + deviceInfo.getType()); 2163 2164 /* Don't expose already exposed active device */ 2165 if (device.equals(mExposedActiveDevice)) { 2166 Log.d(TAG, " onAudioDevicesAdded: " + device + " is already exposed"); 2167 return; 2168 } 2169 2170 if (!device.equals(mActiveDevice)) { 2171 Log.e( 2172 TAG, 2173 "Added device does not match to the one activated here. (" 2174 + device 2175 + " != " 2176 + mActiveDevice 2177 + " / " 2178 + mActiveDevice 2179 + ")"); 2180 continue; 2181 } 2182 2183 mExposedActiveDevice = device; 2184 broadcastActiveDevice(device); 2185 return; 2186 } 2187 } 2188 } 2189 2190 @Override onAudioDevicesRemoved(AudioDeviceInfo[] removedDevices)2191 public void onAudioDevicesRemoved(AudioDeviceInfo[] removedDevices) { 2192 if (mSystemInterface.getAudioManager() == null || mAdapterService == null) { 2193 Log.e(TAG, "Callback called when LeAudioService is stopped"); 2194 return; 2195 } 2196 2197 synchronized (mStateMachines) { 2198 for (AudioDeviceInfo deviceInfo : removedDevices) { 2199 if (deviceInfo.getType() != AudioDeviceInfo.TYPE_BLUETOOTH_SCO) { 2200 continue; 2201 } 2202 2203 String address = deviceInfo.getAddress(); 2204 if (address.equals("00:00:00:00:00:00")) { 2205 continue; 2206 } 2207 2208 mExposedActiveDevice = null; 2209 2210 Log.d( 2211 TAG, 2212 " onAudioDevicesRemoved: " 2213 + address 2214 + ", device type: " 2215 + deviceInfo.getType() 2216 + ", mActiveDevice: " 2217 + mActiveDevice); 2218 } 2219 } 2220 } 2221 } 2222 2223 /** 2224 * Check whether it is OK to accept a headset connection from a remote device 2225 * 2226 * @param device remote device that initiates the connection 2227 * @return true if the connection is acceptable 2228 */ okToAcceptConnection(BluetoothDevice device, boolean isOutgoingRequest)2229 public boolean okToAcceptConnection(BluetoothDevice device, boolean isOutgoingRequest) { 2230 // Check if this is an incoming connection in Quiet mode. 2231 if (mAdapterService.isQuietModeEnabled()) { 2232 Log.w(TAG, "okToAcceptConnection: return false as quiet mode enabled"); 2233 return false; 2234 } 2235 // Check connection policy and accept or reject the connection. 2236 int connectionPolicy = getConnectionPolicy(device); 2237 if (!Flags.donotValidateBondStateFromProfiles()) { 2238 int bondState = mAdapterService.getBondState(device); 2239 // Allow this connection only if the device is bonded. Any attempt to connect while 2240 // bonding would potentially lead to an unauthorized connection. 2241 if (bondState != BluetoothDevice.BOND_BONDED) { 2242 Log.w(TAG, "okToAcceptConnection: return false, bondState=" + bondState); 2243 return false; 2244 } 2245 } 2246 if (connectionPolicy != CONNECTION_POLICY_UNKNOWN 2247 && connectionPolicy != CONNECTION_POLICY_ALLOWED) { 2248 // Otherwise, reject the connection if connection policy is not valid. 2249 if (!isOutgoingRequest) { 2250 A2dpService a2dpService = A2dpService.getA2dpService(); 2251 if (a2dpService != null && a2dpService.okToConnect(device, true)) { 2252 Log.d( 2253 TAG, 2254 "okToAcceptConnection: return false," 2255 + " Fallback connection to allowed A2DP profile"); 2256 a2dpService.connect(device); 2257 return false; 2258 } 2259 } 2260 Log.w(TAG, "okToAcceptConnection: return false, connectionPolicy=" + connectionPolicy); 2261 return false; 2262 } 2263 List<BluetoothDevice> connectingConnectedDevices = 2264 getDevicesMatchingConnectionStates(CONNECTING_CONNECTED_STATES); 2265 if (connectingConnectedDevices.size() >= mMaxHeadsetConnections) { 2266 Log.w( 2267 TAG, 2268 "Maximum number of connections " 2269 + mMaxHeadsetConnections 2270 + " was reached, rejecting connection from " 2271 + device); 2272 return false; 2273 } 2274 return true; 2275 } 2276 2277 /** 2278 * Checks if SCO should be connected at current system state. Returns {@link 2279 * BluetoothStatusCodes#SUCCESS} if SCO is allowed to be connected or an error code on failure. 2280 * 2281 * @param device device for SCO to be connected 2282 * @return whether SCO can be connected 2283 */ isScoAcceptable(BluetoothDevice device)2284 public int isScoAcceptable(BluetoothDevice device) { 2285 synchronized (mStateMachines) { 2286 if (device == null || !device.equals(mActiveDevice)) { 2287 Log.w( 2288 TAG, 2289 "isScoAcceptable: rejected SCO since " 2290 + device 2291 + " is not the current active device " 2292 + mActiveDevice); 2293 return BluetoothStatusCodes.ERROR_NOT_ACTIVE_DEVICE; 2294 } 2295 if (SystemProperties.getBoolean(REJECT_SCO_IF_HFPC_CONNECTED_PROPERTY, false) 2296 && isHeadsetClientConnected()) { 2297 Log.w(TAG, "isScoAcceptable: rejected SCO since HFPC is connected!"); 2298 return BluetoothStatusCodes.ERROR_AUDIO_ROUTE_BLOCKED; 2299 } 2300 if (mForceScoAudio) { 2301 return BluetoothStatusCodes.SUCCESS; 2302 } 2303 if (!mAudioRouteAllowed) { 2304 Log.w(TAG, "isScoAcceptable: rejected SCO since audio route is not allowed"); 2305 return BluetoothStatusCodes.ERROR_AUDIO_ROUTE_BLOCKED; 2306 } 2307 if (mVoiceRecognitionStarted || mVirtualCallStarted) { 2308 return BluetoothStatusCodes.SUCCESS; 2309 } 2310 if (shouldCallAudioBeActive()) { 2311 return BluetoothStatusCodes.SUCCESS; 2312 } 2313 Log.w( 2314 TAG, 2315 "isScoAcceptable: rejected SCO, inCall=" 2316 + mSystemInterface.isInCall() 2317 + ", voiceRecognition=" 2318 + mVoiceRecognitionStarted 2319 + ", ringing=" 2320 + mSystemInterface.isRinging() 2321 + ", inbandRinging=" 2322 + isInbandRingingEnabled() 2323 + ", isVirtualCallStarted=" 2324 + mVirtualCallStarted); 2325 return BluetoothStatusCodes.ERROR_CALL_ACTIVE; 2326 } 2327 } 2328 2329 /** 2330 * Remove state machine in {@link #mStateMachines} for a {@link BluetoothDevice} 2331 * 2332 * @param device device whose state machine is to be removed. 2333 */ removeStateMachine(BluetoothDevice device)2334 void removeStateMachine(BluetoothDevice device) { 2335 synchronized (mStateMachines) { 2336 HeadsetStateMachine stateMachine = mStateMachines.get(device); 2337 if (stateMachine == null) { 2338 Log.w(TAG, "removeStateMachine(), " + device + " does not have a state machine"); 2339 return; 2340 } 2341 Log.i(TAG, "removeStateMachine(), removing state machine for device: " + device); 2342 HeadsetObjectsFactory.getInstance().destroyStateMachine(stateMachine); 2343 mStateMachines.remove(device); 2344 } 2345 } 2346 2347 /** Retrieves the most recently connected device in the A2DP connected devices list. */ getFallbackDevice()2348 public BluetoothDevice getFallbackDevice() { 2349 DatabaseManager dbManager = mAdapterService.getDatabase(); 2350 return dbManager != null 2351 ? dbManager.getMostRecentlyConnectedDevicesInList(getFallbackCandidates(dbManager)) 2352 : null; 2353 } 2354 2355 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) getFallbackCandidates(DatabaseManager dbManager)2356 List<BluetoothDevice> getFallbackCandidates(DatabaseManager dbManager) { 2357 List<BluetoothDevice> fallbackCandidates = getConnectedDevices(); 2358 List<BluetoothDevice> uninterestedCandidates = new ArrayList<>(); 2359 for (BluetoothDevice device : fallbackCandidates) { 2360 if (Utils.isWatch(mAdapterService, device)) { 2361 uninterestedCandidates.add(device); 2362 } 2363 } 2364 for (BluetoothDevice device : uninterestedCandidates) { 2365 fallbackCandidates.remove(device); 2366 } 2367 return fallbackCandidates; 2368 } 2369 2370 @Override dump(StringBuilder sb)2371 public void dump(StringBuilder sb) { 2372 boolean isScoOn = mSystemInterface.getAudioManager().isBluetoothScoOn(); 2373 boolean isInbandRingingSupported = 2374 getResources() 2375 .getBoolean( 2376 com.android.bluetooth.R.bool 2377 .config_bluetooth_hfp_inband_ringing_support); 2378 synchronized (mStateMachines) { 2379 super.dump(sb); 2380 ProfileService.println(sb, "mMaxHeadsetConnections: " + mMaxHeadsetConnections); 2381 ProfileService.println( 2382 sb, 2383 "DefaultMaxHeadsetConnections: " 2384 + mAdapterService.getMaxConnectedAudioDevices()); 2385 ProfileService.println(sb, "mActiveDevice: " + mActiveDevice); 2386 ProfileService.println(sb, "isInbandRingingEnabled: " + isInbandRingingEnabled()); 2387 ProfileService.println(sb, "isInbandRingingSupported: " + isInbandRingingSupported); 2388 ProfileService.println( 2389 sb, "mInbandRingingRuntimeDisable: " + mInbandRingingRuntimeDisable); 2390 ProfileService.println(sb, "mAudioRouteAllowed: " + mAudioRouteAllowed); 2391 ProfileService.println(sb, "mVoiceRecognitionStarted: " + mVoiceRecognitionStarted); 2392 ProfileService.println( 2393 sb, "mVoiceRecognitionTimeoutEvent: " + mVoiceRecognitionTimeoutEvent); 2394 ProfileService.println(sb, "mVirtualCallStarted: " + mVirtualCallStarted); 2395 ProfileService.println(sb, "mDialingOutTimeoutEvent: " + mDialingOutTimeoutEvent); 2396 ProfileService.println(sb, "mForceScoAudio: " + mForceScoAudio); 2397 ProfileService.println(sb, "AudioManager.isBluetoothScoOn(): " + isScoOn); 2398 ProfileService.println(sb, "Telecom.isInCall(): " + mSystemInterface.isInCall()); 2399 ProfileService.println(sb, "Telecom.isRinging(): " + mSystemInterface.isRinging()); 2400 for (HeadsetStateMachine stateMachine : mStateMachines.values()) { 2401 ProfileService.println( 2402 sb, "==== StateMachine for " + stateMachine.getDevice() + " ===="); 2403 stateMachine.dump(sb); 2404 } 2405 } 2406 } 2407 2408 /** Enable SWB Codec. */ enableSwbCodec(int swbCodec, boolean enable, BluetoothDevice device)2409 void enableSwbCodec(int swbCodec, boolean enable, BluetoothDevice device) { 2410 logD("enableSwbCodec: swbCodec: " + swbCodec + " enable: " + enable + " device: " + device); 2411 boolean result = mNativeInterface.enableSwb(swbCodec, enable, device); 2412 logD("enableSwbCodec result: " + result); 2413 } 2414 2415 /** Check whether AptX SWB Codec is enabled. */ isAptXSwbEnabled()2416 boolean isAptXSwbEnabled() { 2417 logD("mIsAptXSwbEnabled: " + mIsAptXSwbEnabled); 2418 return mIsAptXSwbEnabled; 2419 } 2420 2421 /** Check whether AptX SWB Codec Power Management is enabled. */ isAptXSwbPmEnabled()2422 boolean isAptXSwbPmEnabled() { 2423 logD("isAptXSwbPmEnabled: " + mIsAptXSwbPmEnabled); 2424 return mIsAptXSwbPmEnabled; 2425 } 2426 logD(String message)2427 private static void logD(String message) { 2428 Log.d(TAG, message); 2429 } 2430 logScoSessionMetric(BluetoothDevice device, int state, int uuid)2431 public static void logScoSessionMetric(BluetoothDevice device, int state, int uuid) { 2432 MetricsLogger.getInstance() 2433 .logBluetoothEvent( 2434 device, 2435 BluetoothStatsLog 2436 .BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__EVENT_TYPE__SCO_SESSION, 2437 state, 2438 uuid); 2439 } 2440 } 2441