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