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