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