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.MODIFY_PHONE_STATE; 20 21 import android.annotation.Nullable; 22 import android.bluetooth.BluetoothDevice; 23 import android.bluetooth.BluetoothHeadset; 24 import android.bluetooth.BluetoothProfile; 25 import android.bluetooth.BluetoothUuid; 26 import android.bluetooth.IBluetoothHeadset; 27 import android.content.BroadcastReceiver; 28 import android.content.Context; 29 import android.content.Intent; 30 import android.content.IntentFilter; 31 import android.media.AudioManager; 32 import android.net.Uri; 33 import android.os.BatteryManager; 34 import android.os.HandlerThread; 35 import android.os.IDeviceIdleController; 36 import android.os.Looper; 37 import android.os.ParcelUuid; 38 import android.os.RemoteException; 39 import android.os.ServiceManager; 40 import android.os.SystemProperties; 41 import android.os.UserHandle; 42 import android.provider.Settings; 43 import android.telecom.PhoneAccount; 44 import android.util.Log; 45 46 import com.android.bluetooth.BluetoothMetricsProto; 47 import com.android.bluetooth.Utils; 48 import com.android.bluetooth.btservice.AdapterService; 49 import com.android.bluetooth.btservice.MetricsLogger; 50 import com.android.bluetooth.btservice.ProfileService; 51 import com.android.internal.annotations.VisibleForTesting; 52 53 import java.util.ArrayList; 54 import java.util.Arrays; 55 import java.util.Comparator; 56 import java.util.HashMap; 57 import java.util.List; 58 import java.util.Objects; 59 60 /** 61 * Provides Bluetooth Headset and Handsfree profile, as a service in the Bluetooth application. 62 * 63 * Three modes for SCO audio: 64 * Mode 1: Telecom call through {@link #phoneStateChanged(int, int, int, String, int, boolean)} 65 * Mode 2: Virtual call through {@link #startScoUsingVirtualVoiceCall()} 66 * Mode 3: Voice recognition through {@link #startVoiceRecognition(BluetoothDevice)} 67 * 68 * When one mode is active, other mode cannot be started. API user has to terminate existing modes 69 * using the correct API or just {@link #disconnectAudio()} if user is a system service, before 70 * starting a new mode. 71 * 72 * {@link #connectAudio()} will start SCO audio at one of the above modes, but won't change mode 73 * {@link #disconnectAudio()} can happen in any mode to disconnect SCO 74 * 75 * When audio is disconnected, only Mode 1 Telecom call will be persisted, both Mode 2 virtual call 76 * and Mode 3 voice call will be terminated upon SCO termination and client has to restart the mode. 77 * 78 * NOTE: SCO termination can either be initiated on the AG side or the HF side 79 * TODO(b/79660380): As a workaround, voice recognition will be terminated if virtual call or 80 * Telecom call is initiated while voice recognition is ongoing, in case calling app did not call 81 * {@link #stopVoiceRecognition(BluetoothDevice)} 82 * 83 * AG - Audio Gateway, device running this {@link HeadsetService}, e.g. Android Phone 84 * HF - Handsfree device, device running headset client, e.g. Wireless headphones or car kits 85 */ 86 public class HeadsetService extends ProfileService { 87 private static final String TAG = "HeadsetService"; 88 private static final boolean DBG = false; 89 private static final String DISABLE_INBAND_RINGING_PROPERTY = 90 "persist.bluetooth.disableinbandringing"; 91 private static final ParcelUuid[] HEADSET_UUIDS = {BluetoothUuid.HSP, BluetoothUuid.Handsfree}; 92 private static final int[] CONNECTING_CONNECTED_STATES = 93 {BluetoothProfile.STATE_CONNECTING, BluetoothProfile.STATE_CONNECTED}; 94 private static final int DIALING_OUT_TIMEOUT_MS = 10000; 95 96 private int mMaxHeadsetConnections = 1; 97 private BluetoothDevice mActiveDevice; 98 private AdapterService mAdapterService; 99 private HandlerThread mStateMachinesThread; 100 // This is also used as a lock for shared data in HeadsetService 101 private final HashMap<BluetoothDevice, HeadsetStateMachine> mStateMachines = new HashMap<>(); 102 private HeadsetNativeInterface mNativeInterface; 103 private HeadsetSystemInterface mSystemInterface; 104 private boolean mAudioRouteAllowed = true; 105 // Indicates whether SCO audio needs to be forced to open regardless ANY OTHER restrictions 106 private boolean mForceScoAudio; 107 private boolean mInbandRingingRuntimeDisable; 108 private boolean mVirtualCallStarted; 109 // Non null value indicates a pending dialing out event is going on 110 private DialingOutTimeoutEvent mDialingOutTimeoutEvent; 111 private boolean mVoiceRecognitionStarted; 112 // Non null value indicates a pending voice recognition request from headset is going on 113 private VoiceRecognitionTimeoutEvent mVoiceRecognitionTimeoutEvent; 114 // Timeout when voice recognition is started by remote device 115 @VisibleForTesting static int sStartVrTimeoutMs = 5000; 116 private boolean mStarted; 117 private boolean mCreated; 118 private static HeadsetService sHeadsetService; 119 120 @Override 121 public IProfileServiceBinder initBinder() { 122 return new BluetoothHeadsetBinder(this); 123 } 124 125 @Override 126 protected void create() { 127 Log.i(TAG, "create()"); 128 if (mCreated) { 129 throw new IllegalStateException("create() called twice"); 130 } 131 mCreated = true; 132 } 133 134 @Override 135 protected boolean start() { 136 Log.i(TAG, "start()"); 137 if (mStarted) { 138 throw new IllegalStateException("start() called twice"); 139 } 140 // Step 1: Get adapter service, should never be null 141 mAdapterService = Objects.requireNonNull(AdapterService.getAdapterService(), 142 "AdapterService cannot be null when HeadsetService starts"); 143 // Step 2: Start handler thread for state machines 144 mStateMachinesThread = new HandlerThread("HeadsetService.StateMachines"); 145 mStateMachinesThread.start(); 146 // Step 3: Initialize system interface 147 mSystemInterface = HeadsetObjectsFactory.getInstance().makeSystemInterface(this); 148 mSystemInterface.init(); 149 // Step 4: Initialize native interface 150 mMaxHeadsetConnections = mAdapterService.getMaxConnectedAudioDevices(); 151 mNativeInterface = HeadsetObjectsFactory.getInstance().getNativeInterface(); 152 // Add 1 to allow a pending device to be connecting or disconnecting 153 mNativeInterface.init(mMaxHeadsetConnections + 1, isInbandRingingEnabled()); 154 // Step 5: Check if state machine table is empty, crash if not 155 if (mStateMachines.size() > 0) { 156 throw new IllegalStateException( 157 "start(): mStateMachines is not empty, " + mStateMachines.size() 158 + " is already created. Was stop() called properly?"); 159 } 160 // Step 6: Setup broadcast receivers 161 IntentFilter filter = new IntentFilter(); 162 filter.addAction(Intent.ACTION_BATTERY_CHANGED); 163 filter.addAction(AudioManager.VOLUME_CHANGED_ACTION); 164 filter.addAction(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY); 165 filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED); 166 registerReceiver(mHeadsetReceiver, filter); 167 // Step 7: Mark service as started 168 setHeadsetService(this); 169 mStarted = true; 170 return true; 171 } 172 173 @Override 174 protected boolean stop() { 175 Log.i(TAG, "stop()"); 176 if (!mStarted) { 177 Log.w(TAG, "stop() called before start()"); 178 // Still return true because it is considered "stopped" and doesn't have any functional 179 // impact on the user 180 return true; 181 } 182 // Step 7: Mark service as stopped 183 mStarted = false; 184 setHeadsetService(null); 185 // Step 6: Tear down broadcast receivers 186 unregisterReceiver(mHeadsetReceiver); 187 synchronized (mStateMachines) { 188 // Reset active device to null 189 mActiveDevice = null; 190 mInbandRingingRuntimeDisable = false; 191 mForceScoAudio = false; 192 mAudioRouteAllowed = true; 193 mMaxHeadsetConnections = 1; 194 mVoiceRecognitionStarted = false; 195 mVirtualCallStarted = false; 196 if (mDialingOutTimeoutEvent != null) { 197 mStateMachinesThread.getThreadHandler().removeCallbacks(mDialingOutTimeoutEvent); 198 mDialingOutTimeoutEvent = null; 199 } 200 if (mVoiceRecognitionTimeoutEvent != null) { 201 mStateMachinesThread.getThreadHandler() 202 .removeCallbacks(mVoiceRecognitionTimeoutEvent); 203 mVoiceRecognitionTimeoutEvent = null; 204 if (mSystemInterface.getVoiceRecognitionWakeLock().isHeld()) { 205 mSystemInterface.getVoiceRecognitionWakeLock().release(); 206 } 207 } 208 // Step 5: Destroy state machines 209 for (HeadsetStateMachine stateMachine : mStateMachines.values()) { 210 HeadsetObjectsFactory.getInstance().destroyStateMachine(stateMachine); 211 } 212 mStateMachines.clear(); 213 } 214 // Step 4: Destroy native interface 215 mNativeInterface.cleanup(); 216 // Step 3: Destroy system interface 217 mSystemInterface.stop(); 218 // Step 2: Stop handler thread 219 mStateMachinesThread.quitSafely(); 220 mStateMachinesThread = null; 221 // Step 1: Clear 222 mAdapterService = null; 223 return true; 224 } 225 226 @Override 227 protected void cleanup() { 228 Log.i(TAG, "cleanup"); 229 if (!mCreated) { 230 Log.w(TAG, "cleanup() called before create()"); 231 } 232 mCreated = false; 233 } 234 235 /** 236 * Checks if this service object is able to accept binder calls 237 * 238 * @return True if the object can accept binder calls, False otherwise 239 */ 240 public boolean isAlive() { 241 return isAvailable() && mCreated && mStarted; 242 } 243 244 /** 245 * Get the {@link Looper} for the state machine thread. This is used in testing and helper 246 * objects 247 * 248 * @return {@link Looper} for the state machine thread 249 */ 250 @VisibleForTesting 251 public Looper getStateMachinesThreadLooper() { 252 return mStateMachinesThread.getLooper(); 253 } 254 255 interface StateMachineTask { 256 void execute(HeadsetStateMachine stateMachine); 257 } 258 259 private boolean doForStateMachine(BluetoothDevice device, StateMachineTask task) { 260 synchronized (mStateMachines) { 261 HeadsetStateMachine stateMachine = mStateMachines.get(device); 262 if (stateMachine == null) { 263 return false; 264 } 265 task.execute(stateMachine); 266 } 267 return true; 268 } 269 270 private void doForEachConnectedStateMachine(StateMachineTask task) { 271 synchronized (mStateMachines) { 272 for (BluetoothDevice device : getConnectedDevices()) { 273 task.execute(mStateMachines.get(device)); 274 } 275 } 276 } 277 278 void onDeviceStateChanged(HeadsetDeviceState deviceState) { 279 doForEachConnectedStateMachine( 280 stateMachine -> stateMachine.sendMessage(HeadsetStateMachine.DEVICE_STATE_CHANGED, 281 deviceState)); 282 } 283 284 /** 285 * Handle messages from native (JNI) to Java. This needs to be synchronized to avoid posting 286 * messages to state machine before start() is done 287 * 288 * @param stackEvent event from native stack 289 */ 290 void messageFromNative(HeadsetStackEvent stackEvent) { 291 Objects.requireNonNull(stackEvent.device, 292 "Device should never be null, event: " + stackEvent); 293 synchronized (mStateMachines) { 294 HeadsetStateMachine stateMachine = mStateMachines.get(stackEvent.device); 295 if (stackEvent.type == HeadsetStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED) { 296 switch (stackEvent.valueInt) { 297 case HeadsetHalConstants.CONNECTION_STATE_CONNECTED: 298 case HeadsetHalConstants.CONNECTION_STATE_CONNECTING: { 299 // Create new state machine if none is found 300 if (stateMachine == null) { 301 stateMachine = HeadsetObjectsFactory.getInstance() 302 .makeStateMachine(stackEvent.device, 303 mStateMachinesThread.getLooper(), this, mAdapterService, 304 mNativeInterface, mSystemInterface); 305 mStateMachines.put(stackEvent.device, stateMachine); 306 } 307 break; 308 } 309 } 310 } 311 if (stateMachine == null) { 312 throw new IllegalStateException( 313 "State machine not found for stack event: " + stackEvent); 314 } 315 stateMachine.sendMessage(HeadsetStateMachine.STACK_EVENT, stackEvent); 316 } 317 } 318 319 private final BroadcastReceiver mHeadsetReceiver = new BroadcastReceiver() { 320 @Override 321 public void onReceive(Context context, Intent intent) { 322 String action = intent.getAction(); 323 if (action == null) { 324 Log.w(TAG, "mHeadsetReceiver, action is null"); 325 return; 326 } 327 switch (action) { 328 case Intent.ACTION_BATTERY_CHANGED: { 329 int batteryLevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1); 330 int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1); 331 if (batteryLevel < 0 || scale <= 0) { 332 Log.e(TAG, "Bad Battery Changed intent: batteryLevel=" + batteryLevel 333 + ", scale=" + scale); 334 return; 335 } 336 batteryLevel = batteryLevel * 5 / scale; 337 mSystemInterface.getHeadsetPhoneState().setCindBatteryCharge(batteryLevel); 338 break; 339 } 340 case AudioManager.VOLUME_CHANGED_ACTION: { 341 int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1); 342 if (streamType == AudioManager.STREAM_BLUETOOTH_SCO) { 343 doForEachConnectedStateMachine(stateMachine -> stateMachine.sendMessage( 344 HeadsetStateMachine.INTENT_SCO_VOLUME_CHANGED, intent)); 345 } 346 break; 347 } 348 case BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY: { 349 int requestType = intent.getIntExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE, 350 BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS); 351 BluetoothDevice device = 352 intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 353 logD("Received BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY, device=" + device 354 + ", type=" + requestType); 355 if (requestType == BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS) { 356 synchronized (mStateMachines) { 357 final HeadsetStateMachine stateMachine = mStateMachines.get(device); 358 if (stateMachine == null) { 359 Log.wtfStack(TAG, "Cannot find state machine for " + device); 360 return; 361 } 362 stateMachine.sendMessage( 363 HeadsetStateMachine.INTENT_CONNECTION_ACCESS_REPLY, intent); 364 } 365 } 366 break; 367 } 368 case BluetoothDevice.ACTION_BOND_STATE_CHANGED: { 369 int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, 370 BluetoothDevice.ERROR); 371 BluetoothDevice device = Objects.requireNonNull( 372 intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE), 373 "ACTION_BOND_STATE_CHANGED with no EXTRA_DEVICE"); 374 logD("Bond state changed for device: " + device + " state: " + state); 375 if (state != BluetoothDevice.BOND_NONE) { 376 break; 377 } 378 synchronized (mStateMachines) { 379 HeadsetStateMachine stateMachine = mStateMachines.get(device); 380 if (stateMachine == null) { 381 break; 382 } 383 if (stateMachine.getConnectionState() 384 != BluetoothProfile.STATE_DISCONNECTED) { 385 break; 386 } 387 removeStateMachine(device); 388 } 389 break; 390 } 391 default: 392 Log.w(TAG, "Unknown action " + action); 393 } 394 } 395 }; 396 397 /** 398 * Handlers for incoming service calls 399 */ 400 private static class BluetoothHeadsetBinder extends IBluetoothHeadset.Stub 401 implements IProfileServiceBinder { 402 private volatile HeadsetService mService; 403 404 BluetoothHeadsetBinder(HeadsetService svc) { 405 mService = svc; 406 } 407 408 @Override 409 public void cleanup() { 410 mService = null; 411 } 412 413 private HeadsetService getService() { 414 final HeadsetService service = mService; 415 if (!Utils.checkCallerAllowManagedProfiles(service)) { 416 Log.w(TAG, "Headset call not allowed for non-active user"); 417 return null; 418 } 419 if (service == null) { 420 Log.w(TAG, "Service is null"); 421 return null; 422 } 423 if (!service.isAlive()) { 424 Log.w(TAG, "Service is not alive"); 425 return null; 426 } 427 return service; 428 } 429 430 @Override 431 public boolean connect(BluetoothDevice device) { 432 HeadsetService service = getService(); 433 if (service == null) { 434 return false; 435 } 436 return service.connect(device); 437 } 438 439 @Override 440 public boolean disconnect(BluetoothDevice device) { 441 HeadsetService service = getService(); 442 if (service == null) { 443 return false; 444 } 445 return service.disconnect(device); 446 } 447 448 @Override 449 public List<BluetoothDevice> getConnectedDevices() { 450 HeadsetService service = getService(); 451 if (service == null) { 452 return new ArrayList<BluetoothDevice>(0); 453 } 454 return service.getConnectedDevices(); 455 } 456 457 @Override 458 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 459 HeadsetService service = getService(); 460 if (service == null) { 461 return new ArrayList<BluetoothDevice>(0); 462 } 463 return service.getDevicesMatchingConnectionStates(states); 464 } 465 466 @Override 467 public int getConnectionState(BluetoothDevice device) { 468 HeadsetService service = getService(); 469 if (service == null) { 470 return BluetoothProfile.STATE_DISCONNECTED; 471 } 472 return service.getConnectionState(device); 473 } 474 475 @Override 476 public boolean setPriority(BluetoothDevice device, int priority) { 477 HeadsetService service = getService(); 478 if (service == null) { 479 return false; 480 } 481 return service.setPriority(device, priority); 482 } 483 484 @Override 485 public int getPriority(BluetoothDevice device) { 486 HeadsetService service = getService(); 487 if (service == null) { 488 return BluetoothProfile.PRIORITY_UNDEFINED; 489 } 490 return service.getPriority(device); 491 } 492 493 @Override 494 public boolean startVoiceRecognition(BluetoothDevice device) { 495 HeadsetService service = getService(); 496 if (service == null) { 497 return false; 498 } 499 return service.startVoiceRecognition(device); 500 } 501 502 @Override 503 public boolean stopVoiceRecognition(BluetoothDevice device) { 504 HeadsetService service = getService(); 505 if (service == null) { 506 return false; 507 } 508 return service.stopVoiceRecognition(device); 509 } 510 511 @Override 512 public boolean isAudioOn() { 513 HeadsetService service = getService(); 514 if (service == null) { 515 return false; 516 } 517 return service.isAudioOn(); 518 } 519 520 @Override 521 public boolean isAudioConnected(BluetoothDevice device) { 522 HeadsetService service = getService(); 523 if (service == null) { 524 return false; 525 } 526 return service.isAudioConnected(device); 527 } 528 529 @Override 530 public int getAudioState(BluetoothDevice device) { 531 HeadsetService service = getService(); 532 if (service == null) { 533 return BluetoothHeadset.STATE_AUDIO_DISCONNECTED; 534 } 535 return service.getAudioState(device); 536 } 537 538 @Override 539 public boolean connectAudio() { 540 HeadsetService service = getService(); 541 if (service == null) { 542 return false; 543 } 544 return service.connectAudio(); 545 } 546 547 @Override 548 public boolean disconnectAudio() { 549 HeadsetService service = getService(); 550 if (service == null) { 551 return false; 552 } 553 return service.disconnectAudio(); 554 } 555 556 @Override 557 public void setAudioRouteAllowed(boolean allowed) { 558 HeadsetService service = getService(); 559 if (service == null) { 560 return; 561 } 562 service.setAudioRouteAllowed(allowed); 563 } 564 565 @Override 566 public boolean getAudioRouteAllowed() { 567 HeadsetService service = getService(); 568 if (service != null) { 569 return service.getAudioRouteAllowed(); 570 } 571 return false; 572 } 573 574 @Override 575 public void setForceScoAudio(boolean forced) { 576 HeadsetService service = getService(); 577 if (service == null) { 578 return; 579 } 580 service.setForceScoAudio(forced); 581 } 582 583 @Override 584 public boolean startScoUsingVirtualVoiceCall() { 585 HeadsetService service = getService(); 586 if (service == null) { 587 return false; 588 } 589 return service.startScoUsingVirtualVoiceCall(); 590 } 591 592 @Override 593 public boolean stopScoUsingVirtualVoiceCall() { 594 HeadsetService service = getService(); 595 if (service == null) { 596 return false; 597 } 598 return service.stopScoUsingVirtualVoiceCall(); 599 } 600 601 @Override 602 public void phoneStateChanged(int numActive, int numHeld, int callState, String number, 603 int type) { 604 HeadsetService service = getService(); 605 if (service == null) { 606 return; 607 } 608 service.phoneStateChanged(numActive, numHeld, callState, number, type, false); 609 } 610 611 @Override 612 public void clccResponse(int index, int direction, int status, int mode, boolean mpty, 613 String number, int type) { 614 HeadsetService service = getService(); 615 if (service == null) { 616 return; 617 } 618 service.clccResponse(index, direction, status, mode, mpty, number, type); 619 } 620 621 @Override 622 public boolean sendVendorSpecificResultCode(BluetoothDevice device, String command, 623 String arg) { 624 HeadsetService service = getService(); 625 if (service == null) { 626 return false; 627 } 628 return service.sendVendorSpecificResultCode(device, command, arg); 629 } 630 631 @Override 632 public boolean setActiveDevice(BluetoothDevice device) { 633 HeadsetService service = getService(); 634 if (service == null) { 635 return false; 636 } 637 return service.setActiveDevice(device); 638 } 639 640 @Override 641 public BluetoothDevice getActiveDevice() { 642 HeadsetService service = getService(); 643 if (service == null) { 644 return null; 645 } 646 return service.getActiveDevice(); 647 } 648 649 @Override 650 public boolean isInbandRingingEnabled() { 651 HeadsetService service = getService(); 652 if (service == null) { 653 return false; 654 } 655 return service.isInbandRingingEnabled(); 656 } 657 } 658 659 // API methods 660 public static synchronized HeadsetService getHeadsetService() { 661 if (sHeadsetService == null) { 662 Log.w(TAG, "getHeadsetService(): service is NULL"); 663 return null; 664 } 665 if (!sHeadsetService.isAvailable()) { 666 Log.w(TAG, "getHeadsetService(): service is not available"); 667 return null; 668 } 669 logD("getHeadsetService(): returning " + sHeadsetService); 670 return sHeadsetService; 671 } 672 673 private static synchronized void setHeadsetService(HeadsetService instance) { 674 logD("setHeadsetService(): set to: " + instance); 675 sHeadsetService = instance; 676 } 677 678 public boolean connect(BluetoothDevice device) { 679 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission"); 680 if (getPriority(device) == BluetoothProfile.PRIORITY_OFF) { 681 Log.w(TAG, "connect: PRIORITY_OFF, device=" + device + ", " + Utils.getUidPidString()); 682 return false; 683 } 684 ParcelUuid[] featureUuids = mAdapterService.getRemoteUuids(device); 685 if (!BluetoothUuid.containsAnyUuid(featureUuids, HEADSET_UUIDS)) { 686 Log.e(TAG, "connect: Cannot connect to " + device + ": no headset UUID, " 687 + Utils.getUidPidString()); 688 return false; 689 } 690 synchronized (mStateMachines) { 691 Log.i(TAG, "connect: device=" + device + ", " + Utils.getUidPidString()); 692 HeadsetStateMachine stateMachine = mStateMachines.get(device); 693 if (stateMachine == null) { 694 stateMachine = HeadsetObjectsFactory.getInstance() 695 .makeStateMachine(device, mStateMachinesThread.getLooper(), this, 696 mAdapterService, mNativeInterface, mSystemInterface); 697 mStateMachines.put(device, stateMachine); 698 } 699 int connectionState = stateMachine.getConnectionState(); 700 if (connectionState == BluetoothProfile.STATE_CONNECTED 701 || connectionState == BluetoothProfile.STATE_CONNECTING) { 702 Log.w(TAG, "connect: device " + device 703 + " is already connected/connecting, connectionState=" + connectionState); 704 return false; 705 } 706 List<BluetoothDevice> connectingConnectedDevices = 707 getDevicesMatchingConnectionStates(CONNECTING_CONNECTED_STATES); 708 boolean disconnectExisting = false; 709 if (connectingConnectedDevices.size() >= mMaxHeadsetConnections) { 710 // When there is maximum one device, we automatically disconnect the current one 711 if (mMaxHeadsetConnections == 1) { 712 disconnectExisting = true; 713 } else { 714 Log.w(TAG, "Max connection has reached, rejecting connection to " + device); 715 return false; 716 } 717 } 718 if (disconnectExisting) { 719 for (BluetoothDevice connectingConnectedDevice : connectingConnectedDevices) { 720 disconnect(connectingConnectedDevice); 721 } 722 setActiveDevice(null); 723 } 724 stateMachine.sendMessage(HeadsetStateMachine.CONNECT, device); 725 } 726 return true; 727 } 728 729 boolean disconnect(BluetoothDevice device) { 730 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); 731 Log.i(TAG, "disconnect: device=" + device + ", " + Utils.getUidPidString()); 732 synchronized (mStateMachines) { 733 HeadsetStateMachine stateMachine = mStateMachines.get(device); 734 if (stateMachine == null) { 735 Log.w(TAG, "disconnect: device " + device + " not ever connected/connecting"); 736 return false; 737 } 738 int connectionState = stateMachine.getConnectionState(); 739 if (connectionState != BluetoothProfile.STATE_CONNECTED 740 && connectionState != BluetoothProfile.STATE_CONNECTING) { 741 Log.w(TAG, "disconnect: device " + device 742 + " not connected/connecting, connectionState=" + connectionState); 743 return false; 744 } 745 stateMachine.sendMessage(HeadsetStateMachine.DISCONNECT, device); 746 } 747 return true; 748 } 749 750 public List<BluetoothDevice> getConnectedDevices() { 751 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 752 ArrayList<BluetoothDevice> devices = new ArrayList<>(); 753 synchronized (mStateMachines) { 754 for (HeadsetStateMachine stateMachine : mStateMachines.values()) { 755 if (stateMachine.getConnectionState() == BluetoothProfile.STATE_CONNECTED) { 756 devices.add(stateMachine.getDevice()); 757 } 758 } 759 } 760 return devices; 761 } 762 763 /** 764 * Same as the API method {@link BluetoothHeadset#getDevicesMatchingConnectionStates(int[])} 765 * 766 * @param states an array of states from {@link BluetoothProfile} 767 * @return a list of devices matching the array of connection states 768 */ 769 @VisibleForTesting 770 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 771 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 772 ArrayList<BluetoothDevice> devices = new ArrayList<>(); 773 if (states == null) { 774 return devices; 775 } 776 final BluetoothDevice[] bondedDevices = mAdapterService.getBondedDevices(); 777 if (bondedDevices == null) { 778 return devices; 779 } 780 synchronized (mStateMachines) { 781 for (BluetoothDevice device : bondedDevices) { 782 final ParcelUuid[] featureUuids = mAdapterService.getRemoteUuids(device); 783 if (!BluetoothUuid.containsAnyUuid(featureUuids, HEADSET_UUIDS)) { 784 continue; 785 } 786 int connectionState = getConnectionState(device); 787 for (int state : states) { 788 if (connectionState == state) { 789 devices.add(device); 790 break; 791 } 792 } 793 } 794 } 795 return devices; 796 } 797 798 public int getConnectionState(BluetoothDevice device) { 799 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 800 synchronized (mStateMachines) { 801 final HeadsetStateMachine stateMachine = mStateMachines.get(device); 802 if (stateMachine == null) { 803 return BluetoothProfile.STATE_DISCONNECTED; 804 } 805 return stateMachine.getConnectionState(); 806 } 807 } 808 809 public boolean setPriority(BluetoothDevice device, int priority) { 810 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); 811 Settings.Global.putInt(getContentResolver(), 812 Settings.Global.getBluetoothHeadsetPriorityKey(device.getAddress()), priority); 813 Log.i(TAG, "setPriority: device=" + device + ", priority=" + priority + ", " 814 + Utils.getUidPidString()); 815 return true; 816 } 817 818 public int getPriority(BluetoothDevice device) { 819 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 820 return Settings.Global.getInt(getContentResolver(), 821 Settings.Global.getBluetoothHeadsetPriorityKey(device.getAddress()), 822 BluetoothProfile.PRIORITY_UNDEFINED); 823 } 824 825 boolean startVoiceRecognition(BluetoothDevice device) { 826 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 827 Log.i(TAG, "startVoiceRecognition: device=" + device + ", " + Utils.getUidPidString()); 828 synchronized (mStateMachines) { 829 // TODO(b/79660380): Workaround in case voice recognition was not terminated properly 830 if (mVoiceRecognitionStarted) { 831 boolean status = stopVoiceRecognition(mActiveDevice); 832 Log.w(TAG, "startVoiceRecognition: voice recognition is still active, just called " 833 + "stopVoiceRecognition, returned " + status + " on " + mActiveDevice 834 + ", please try again"); 835 mVoiceRecognitionStarted = false; 836 return false; 837 } 838 if (!isAudioModeIdle()) { 839 Log.w(TAG, "startVoiceRecognition: audio mode not idle, active device is " 840 + mActiveDevice); 841 return false; 842 } 843 // Audio should not be on when no audio mode is active 844 if (isAudioOn()) { 845 // Disconnect audio so that API user can try later 846 boolean status = disconnectAudio(); 847 Log.w(TAG, "startVoiceRecognition: audio is still active, please wait for audio to" 848 + " be disconnected, disconnectAudio() returned " + status 849 + ", active device is " + mActiveDevice); 850 return false; 851 } 852 if (device == null) { 853 Log.i(TAG, "device is null, use active device " + mActiveDevice + " instead"); 854 device = mActiveDevice; 855 } 856 boolean pendingRequestByHeadset = false; 857 if (mVoiceRecognitionTimeoutEvent != null) { 858 if (!mVoiceRecognitionTimeoutEvent.mVoiceRecognitionDevice.equals(device)) { 859 // TODO(b/79660380): Workaround when target device != requesting device 860 Log.w(TAG, "startVoiceRecognition: device " + device 861 + " is not the same as requesting device " 862 + mVoiceRecognitionTimeoutEvent.mVoiceRecognitionDevice 863 + ", fall back to requesting device"); 864 device = mVoiceRecognitionTimeoutEvent.mVoiceRecognitionDevice; 865 } 866 mStateMachinesThread.getThreadHandler() 867 .removeCallbacks(mVoiceRecognitionTimeoutEvent); 868 mVoiceRecognitionTimeoutEvent = null; 869 if (mSystemInterface.getVoiceRecognitionWakeLock().isHeld()) { 870 mSystemInterface.getVoiceRecognitionWakeLock().release(); 871 } 872 pendingRequestByHeadset = true; 873 } 874 if (!Objects.equals(device, mActiveDevice) && !setActiveDevice(device)) { 875 Log.w(TAG, "startVoiceRecognition: failed to set " + device + " as active"); 876 return false; 877 } 878 final HeadsetStateMachine stateMachine = mStateMachines.get(device); 879 if (stateMachine == null) { 880 Log.w(TAG, "startVoiceRecognition: " + device + " is never connected"); 881 return false; 882 } 883 int connectionState = stateMachine.getConnectionState(); 884 if (connectionState != BluetoothProfile.STATE_CONNECTED 885 && connectionState != BluetoothProfile.STATE_CONNECTING) { 886 Log.w(TAG, "startVoiceRecognition: " + device + " is not connected or connecting"); 887 return false; 888 } 889 mVoiceRecognitionStarted = true; 890 if (pendingRequestByHeadset) { 891 stateMachine.sendMessage(HeadsetStateMachine.VOICE_RECOGNITION_RESULT, 892 1 /* success */, 0, device); 893 } else { 894 stateMachine.sendMessage(HeadsetStateMachine.VOICE_RECOGNITION_START, device); 895 } 896 stateMachine.sendMessage(HeadsetStateMachine.CONNECT_AUDIO, device); 897 } 898 return true; 899 } 900 901 boolean stopVoiceRecognition(BluetoothDevice device) { 902 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 903 Log.i(TAG, "stopVoiceRecognition: device=" + device + ", " + Utils.getUidPidString()); 904 synchronized (mStateMachines) { 905 if (!Objects.equals(mActiveDevice, device)) { 906 Log.w(TAG, "startVoiceRecognition: requested device " + device 907 + " is not active, use active device " + mActiveDevice + " instead"); 908 device = mActiveDevice; 909 } 910 final HeadsetStateMachine stateMachine = mStateMachines.get(device); 911 if (stateMachine == null) { 912 Log.w(TAG, "stopVoiceRecognition: " + device + " is never connected"); 913 return false; 914 } 915 int connectionState = stateMachine.getConnectionState(); 916 if (connectionState != BluetoothProfile.STATE_CONNECTED 917 && connectionState != BluetoothProfile.STATE_CONNECTING) { 918 Log.w(TAG, "stopVoiceRecognition: " + device + " is not connected or connecting"); 919 return false; 920 } 921 if (!mVoiceRecognitionStarted) { 922 Log.w(TAG, "stopVoiceRecognition: voice recognition was not started"); 923 return false; 924 } 925 mVoiceRecognitionStarted = false; 926 stateMachine.sendMessage(HeadsetStateMachine.VOICE_RECOGNITION_STOP, device); 927 stateMachine.sendMessage(HeadsetStateMachine.DISCONNECT_AUDIO, device); 928 } 929 return true; 930 } 931 932 boolean isAudioOn() { 933 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 934 return getNonIdleAudioDevices().size() > 0; 935 } 936 937 boolean isAudioConnected(BluetoothDevice device) { 938 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 939 synchronized (mStateMachines) { 940 final HeadsetStateMachine stateMachine = mStateMachines.get(device); 941 if (stateMachine == null) { 942 return false; 943 } 944 return stateMachine.getAudioState() == BluetoothHeadset.STATE_AUDIO_CONNECTED; 945 } 946 } 947 948 int getAudioState(BluetoothDevice device) { 949 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 950 synchronized (mStateMachines) { 951 final HeadsetStateMachine stateMachine = mStateMachines.get(device); 952 if (stateMachine == null) { 953 return BluetoothHeadset.STATE_AUDIO_DISCONNECTED; 954 } 955 return stateMachine.getAudioState(); 956 } 957 } 958 959 public void setAudioRouteAllowed(boolean allowed) { 960 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); 961 Log.i(TAG, "setAudioRouteAllowed: allowed=" + allowed + ", " + Utils.getUidPidString()); 962 mAudioRouteAllowed = allowed; 963 mNativeInterface.setScoAllowed(allowed); 964 } 965 966 public boolean getAudioRouteAllowed() { 967 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 968 return mAudioRouteAllowed; 969 } 970 971 public void setForceScoAudio(boolean forced) { 972 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); 973 Log.i(TAG, "setForceScoAudio: forced=" + forced + ", " + Utils.getUidPidString()); 974 mForceScoAudio = forced; 975 } 976 977 @VisibleForTesting 978 public boolean getForceScoAudio() { 979 return mForceScoAudio; 980 } 981 982 /** 983 * Get first available device for SCO audio 984 * 985 * @return first connected headset device 986 */ 987 @VisibleForTesting 988 @Nullable 989 public BluetoothDevice getFirstConnectedAudioDevice() { 990 ArrayList<HeadsetStateMachine> stateMachines = new ArrayList<>(); 991 synchronized (mStateMachines) { 992 List<BluetoothDevice> availableDevices = 993 getDevicesMatchingConnectionStates(CONNECTING_CONNECTED_STATES); 994 for (BluetoothDevice device : availableDevices) { 995 final HeadsetStateMachine stateMachine = mStateMachines.get(device); 996 if (stateMachine == null) { 997 continue; 998 } 999 stateMachines.add(stateMachine); 1000 } 1001 } 1002 stateMachines.sort(Comparator.comparingLong(HeadsetStateMachine::getConnectingTimestampMs)); 1003 if (stateMachines.size() > 0) { 1004 return stateMachines.get(0).getDevice(); 1005 } 1006 return null; 1007 } 1008 1009 /** 1010 * Set the active device. 1011 * 1012 * @param device the active device 1013 * @return true on success, otherwise false 1014 */ 1015 public boolean setActiveDevice(BluetoothDevice device) { 1016 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); 1017 Log.i(TAG, "setActiveDevice: device=" + device + ", " + Utils.getUidPidString()); 1018 synchronized (mStateMachines) { 1019 if (device == null) { 1020 // Clear the active device 1021 if (mVoiceRecognitionStarted) { 1022 if (!stopVoiceRecognition(mActiveDevice)) { 1023 Log.w(TAG, "setActiveDevice: fail to stopVoiceRecognition from " 1024 + mActiveDevice); 1025 } 1026 } 1027 if (mVirtualCallStarted) { 1028 if (!stopScoUsingVirtualVoiceCall()) { 1029 Log.w(TAG, "setActiveDevice: fail to stopScoUsingVirtualVoiceCall from " 1030 + mActiveDevice); 1031 } 1032 } 1033 if (getAudioState(mActiveDevice) != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { 1034 if (!disconnectAudio(mActiveDevice)) { 1035 Log.w(TAG, "setActiveDevice: disconnectAudio failed on " + mActiveDevice); 1036 } 1037 } 1038 mActiveDevice = null; 1039 broadcastActiveDevice(null); 1040 return true; 1041 } 1042 if (device.equals(mActiveDevice)) { 1043 Log.i(TAG, "setActiveDevice: device " + device + " is already active"); 1044 return true; 1045 } 1046 if (getConnectionState(device) != BluetoothProfile.STATE_CONNECTED) { 1047 Log.e(TAG, "setActiveDevice: Cannot set " + device 1048 + " as active, device is not connected"); 1049 return false; 1050 } 1051 if (!mNativeInterface.setActiveDevice(device)) { 1052 Log.e(TAG, "setActiveDevice: Cannot set " + device + " as active in native layer"); 1053 return false; 1054 } 1055 BluetoothDevice previousActiveDevice = mActiveDevice; 1056 mActiveDevice = device; 1057 if (getAudioState(previousActiveDevice) != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { 1058 if (!disconnectAudio(previousActiveDevice)) { 1059 Log.e(TAG, "setActiveDevice: fail to disconnectAudio from " 1060 + previousActiveDevice); 1061 mActiveDevice = previousActiveDevice; 1062 mNativeInterface.setActiveDevice(previousActiveDevice); 1063 return false; 1064 } 1065 broadcastActiveDevice(mActiveDevice); 1066 } else if (shouldPersistAudio()) { 1067 broadcastActiveDevice(mActiveDevice); 1068 if (!connectAudio(mActiveDevice)) { 1069 Log.e(TAG, "setActiveDevice: fail to connectAudio to " + mActiveDevice); 1070 mActiveDevice = previousActiveDevice; 1071 mNativeInterface.setActiveDevice(previousActiveDevice); 1072 return false; 1073 } 1074 } else { 1075 broadcastActiveDevice(mActiveDevice); 1076 } 1077 } 1078 return true; 1079 } 1080 1081 /** 1082 * Get the active device. 1083 * 1084 * @return the active device or null if no device is active 1085 */ 1086 public BluetoothDevice getActiveDevice() { 1087 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 1088 synchronized (mStateMachines) { 1089 return mActiveDevice; 1090 } 1091 } 1092 1093 boolean connectAudio() { 1094 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); 1095 synchronized (mStateMachines) { 1096 BluetoothDevice device = mActiveDevice; 1097 if (device == null) { 1098 Log.w(TAG, "connectAudio: no active device, " + Utils.getUidPidString()); 1099 return false; 1100 } 1101 return connectAudio(device); 1102 } 1103 } 1104 1105 boolean connectAudio(BluetoothDevice device) { 1106 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); 1107 Log.i(TAG, "connectAudio: device=" + device + ", " + Utils.getUidPidString()); 1108 synchronized (mStateMachines) { 1109 if (!isScoAcceptable(device)) { 1110 Log.w(TAG, "connectAudio, rejected SCO request to " + device); 1111 return false; 1112 } 1113 final HeadsetStateMachine stateMachine = mStateMachines.get(device); 1114 if (stateMachine == null) { 1115 Log.w(TAG, "connectAudio: device " + device + " was never connected/connecting"); 1116 return false; 1117 } 1118 if (stateMachine.getConnectionState() != BluetoothProfile.STATE_CONNECTED) { 1119 Log.w(TAG, "connectAudio: profile not connected"); 1120 return false; 1121 } 1122 if (stateMachine.getAudioState() != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { 1123 logD("connectAudio: audio is not idle for device " + device); 1124 return true; 1125 } 1126 if (isAudioOn()) { 1127 Log.w(TAG, "connectAudio: audio is not idle, current audio devices are " 1128 + Arrays.toString(getNonIdleAudioDevices().toArray())); 1129 return false; 1130 } 1131 stateMachine.sendMessage(HeadsetStateMachine.CONNECT_AUDIO, device); 1132 } 1133 return true; 1134 } 1135 1136 private List<BluetoothDevice> getNonIdleAudioDevices() { 1137 ArrayList<BluetoothDevice> devices = new ArrayList<>(); 1138 synchronized (mStateMachines) { 1139 for (HeadsetStateMachine stateMachine : mStateMachines.values()) { 1140 if (stateMachine.getAudioState() != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { 1141 devices.add(stateMachine.getDevice()); 1142 } 1143 } 1144 } 1145 return devices; 1146 } 1147 1148 boolean disconnectAudio() { 1149 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); 1150 boolean result = false; 1151 synchronized (mStateMachines) { 1152 for (BluetoothDevice device : getNonIdleAudioDevices()) { 1153 if (disconnectAudio(device)) { 1154 result = true; 1155 } else { 1156 Log.e(TAG, "disconnectAudio() from " + device + " failed"); 1157 } 1158 } 1159 } 1160 if (!result) { 1161 logD("disconnectAudio() no active audio connection"); 1162 } 1163 return result; 1164 } 1165 1166 boolean disconnectAudio(BluetoothDevice device) { 1167 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); 1168 synchronized (mStateMachines) { 1169 Log.i(TAG, "disconnectAudio: device=" + device + ", " + Utils.getUidPidString()); 1170 final HeadsetStateMachine stateMachine = mStateMachines.get(device); 1171 if (stateMachine == null) { 1172 Log.w(TAG, "disconnectAudio: device " + device + " was never connected/connecting"); 1173 return false; 1174 } 1175 if (stateMachine.getAudioState() == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { 1176 Log.w(TAG, "disconnectAudio, audio is already disconnected for " + device); 1177 return false; 1178 } 1179 stateMachine.sendMessage(HeadsetStateMachine.DISCONNECT_AUDIO, device); 1180 } 1181 return true; 1182 } 1183 1184 boolean isVirtualCallStarted() { 1185 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 1186 synchronized (mStateMachines) { 1187 return mVirtualCallStarted; 1188 } 1189 } 1190 1191 private boolean startScoUsingVirtualVoiceCall() { 1192 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); 1193 Log.i(TAG, "startScoUsingVirtualVoiceCall: " + Utils.getUidPidString()); 1194 synchronized (mStateMachines) { 1195 // TODO(b/79660380): Workaround in case voice recognition was not terminated properly 1196 if (mVoiceRecognitionStarted) { 1197 boolean status = stopVoiceRecognition(mActiveDevice); 1198 Log.w(TAG, "startScoUsingVirtualVoiceCall: voice recognition is still active, " 1199 + "just called stopVoiceRecognition, returned " + status + " on " 1200 + mActiveDevice + ", please try again"); 1201 mVoiceRecognitionStarted = false; 1202 return false; 1203 } 1204 if (!isAudioModeIdle()) { 1205 Log.w(TAG, "startScoUsingVirtualVoiceCall: audio mode not idle, active device is " 1206 + mActiveDevice); 1207 return false; 1208 } 1209 // Audio should not be on when no audio mode is active 1210 if (isAudioOn()) { 1211 // Disconnect audio so that API user can try later 1212 boolean status = disconnectAudio(); 1213 Log.w(TAG, "startScoUsingVirtualVoiceCall: audio is still active, please wait for " 1214 + "audio to be disconnected, disconnectAudio() returned " + status 1215 + ", active device is " + mActiveDevice); 1216 return false; 1217 } 1218 if (mActiveDevice == null) { 1219 Log.w(TAG, "startScoUsingVirtualVoiceCall: no active device"); 1220 return false; 1221 } 1222 mVirtualCallStarted = true; 1223 // Send virtual phone state changed to initialize SCO 1224 phoneStateChanged(0, 0, HeadsetHalConstants.CALL_STATE_DIALING, "", 0, true); 1225 phoneStateChanged(0, 0, HeadsetHalConstants.CALL_STATE_ALERTING, "", 0, true); 1226 phoneStateChanged(1, 0, HeadsetHalConstants.CALL_STATE_IDLE, "", 0, true); 1227 return true; 1228 } 1229 } 1230 1231 boolean stopScoUsingVirtualVoiceCall() { 1232 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); 1233 Log.i(TAG, "stopScoUsingVirtualVoiceCall: " + Utils.getUidPidString()); 1234 synchronized (mStateMachines) { 1235 // 1. Check if virtual call has already started 1236 if (!mVirtualCallStarted) { 1237 Log.w(TAG, "stopScoUsingVirtualVoiceCall: virtual call not started"); 1238 return false; 1239 } 1240 mVirtualCallStarted = false; 1241 // 2. Send virtual phone state changed to close SCO 1242 phoneStateChanged(0, 0, HeadsetHalConstants.CALL_STATE_IDLE, "", 0, true); 1243 } 1244 return true; 1245 } 1246 1247 class DialingOutTimeoutEvent implements Runnable { 1248 BluetoothDevice mDialingOutDevice; 1249 1250 DialingOutTimeoutEvent(BluetoothDevice fromDevice) { 1251 mDialingOutDevice = fromDevice; 1252 } 1253 1254 @Override 1255 public void run() { 1256 synchronized (mStateMachines) { 1257 mDialingOutTimeoutEvent = null; 1258 doForStateMachine(mDialingOutDevice, stateMachine -> stateMachine.sendMessage( 1259 HeadsetStateMachine.DIALING_OUT_RESULT, 0 /* fail */, 0, 1260 mDialingOutDevice)); 1261 } 1262 } 1263 1264 @Override 1265 public String toString() { 1266 return "DialingOutTimeoutEvent[" + mDialingOutDevice + "]"; 1267 } 1268 } 1269 1270 /** 1271 * Dial an outgoing call as requested by the remote device 1272 * 1273 * @param fromDevice remote device that initiated this dial out action 1274 * @param dialNumber number to dial 1275 * @return true on successful dial out 1276 */ 1277 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) 1278 public boolean dialOutgoingCall(BluetoothDevice fromDevice, String dialNumber) { 1279 synchronized (mStateMachines) { 1280 Log.i(TAG, "dialOutgoingCall: from " + fromDevice); 1281 if (!isOnStateMachineThread()) { 1282 Log.e(TAG, "dialOutgoingCall must be called from state machine thread"); 1283 return false; 1284 } 1285 if (mDialingOutTimeoutEvent != null) { 1286 Log.e(TAG, "dialOutgoingCall, already dialing by " + mDialingOutTimeoutEvent); 1287 return false; 1288 } 1289 if (isVirtualCallStarted()) { 1290 if (!stopScoUsingVirtualVoiceCall()) { 1291 Log.e(TAG, "dialOutgoingCall failed to stop current virtual call"); 1292 return false; 1293 } 1294 } 1295 if (!setActiveDevice(fromDevice)) { 1296 Log.e(TAG, "dialOutgoingCall failed to set active device to " + fromDevice); 1297 return false; 1298 } 1299 Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED, 1300 Uri.fromParts(PhoneAccount.SCHEME_TEL, dialNumber, null)); 1301 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 1302 startActivity(intent); 1303 mDialingOutTimeoutEvent = new DialingOutTimeoutEvent(fromDevice); 1304 mStateMachinesThread.getThreadHandler() 1305 .postDelayed(mDialingOutTimeoutEvent, DIALING_OUT_TIMEOUT_MS); 1306 return true; 1307 } 1308 } 1309 1310 /** 1311 * Check if any connected headset has started dialing calls 1312 * 1313 * @return true if some device has started dialing calls 1314 */ 1315 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) 1316 public boolean hasDeviceInitiatedDialingOut() { 1317 synchronized (mStateMachines) { 1318 return mDialingOutTimeoutEvent != null; 1319 } 1320 } 1321 1322 class VoiceRecognitionTimeoutEvent implements Runnable { 1323 BluetoothDevice mVoiceRecognitionDevice; 1324 1325 VoiceRecognitionTimeoutEvent(BluetoothDevice device) { 1326 mVoiceRecognitionDevice = device; 1327 } 1328 1329 @Override 1330 public void run() { 1331 synchronized (mStateMachines) { 1332 if (mSystemInterface.getVoiceRecognitionWakeLock().isHeld()) { 1333 mSystemInterface.getVoiceRecognitionWakeLock().release(); 1334 } 1335 mVoiceRecognitionTimeoutEvent = null; 1336 doForStateMachine(mVoiceRecognitionDevice, stateMachine -> stateMachine.sendMessage( 1337 HeadsetStateMachine.VOICE_RECOGNITION_RESULT, 0 /* fail */, 0, 1338 mVoiceRecognitionDevice)); 1339 } 1340 } 1341 1342 @Override 1343 public String toString() { 1344 return "VoiceRecognitionTimeoutEvent[" + mVoiceRecognitionDevice + "]"; 1345 } 1346 } 1347 1348 boolean startVoiceRecognitionByHeadset(BluetoothDevice fromDevice) { 1349 synchronized (mStateMachines) { 1350 Log.i(TAG, "startVoiceRecognitionByHeadset: from " + fromDevice); 1351 // TODO(b/79660380): Workaround in case voice recognition was not terminated properly 1352 if (mVoiceRecognitionStarted) { 1353 boolean status = stopVoiceRecognition(mActiveDevice); 1354 Log.w(TAG, "startVoiceRecognitionByHeadset: voice recognition is still active, " 1355 + "just called stopVoiceRecognition, returned " + status + " on " 1356 + mActiveDevice + ", please try again"); 1357 mVoiceRecognitionStarted = false; 1358 return false; 1359 } 1360 if (fromDevice == null) { 1361 Log.e(TAG, "startVoiceRecognitionByHeadset: fromDevice is null"); 1362 return false; 1363 } 1364 if (!isAudioModeIdle()) { 1365 Log.w(TAG, "startVoiceRecognitionByHeadset: audio mode not idle, active device is " 1366 + mActiveDevice); 1367 return false; 1368 } 1369 // Audio should not be on when no audio mode is active 1370 if (isAudioOn()) { 1371 // Disconnect audio so that user can try later 1372 boolean status = disconnectAudio(); 1373 Log.w(TAG, "startVoiceRecognitionByHeadset: audio is still active, please wait for" 1374 + " audio to be disconnected, disconnectAudio() returned " + status 1375 + ", active device is " + mActiveDevice); 1376 return false; 1377 } 1378 // Do not start new request until the current one is finished or timeout 1379 if (mVoiceRecognitionTimeoutEvent != null) { 1380 Log.w(TAG, "startVoiceRecognitionByHeadset: failed request from " + fromDevice 1381 + ", already pending by " + mVoiceRecognitionTimeoutEvent); 1382 return false; 1383 } 1384 if (!setActiveDevice(fromDevice)) { 1385 Log.w(TAG, "startVoiceRecognitionByHeadset: failed to set " + fromDevice 1386 + " as active"); 1387 return false; 1388 } 1389 IDeviceIdleController deviceIdleController = IDeviceIdleController.Stub.asInterface( 1390 ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER)); 1391 if (deviceIdleController == null) { 1392 Log.w(TAG, "startVoiceRecognitionByHeadset: deviceIdleController is null, device=" 1393 + fromDevice); 1394 return false; 1395 } 1396 try { 1397 deviceIdleController.exitIdle("voice-command"); 1398 } catch (RemoteException e) { 1399 Log.w(TAG, 1400 "startVoiceRecognitionByHeadset: failed to exit idle, device=" + fromDevice 1401 + ", error=" + e.getMessage()); 1402 return false; 1403 } 1404 if (!mSystemInterface.activateVoiceRecognition()) { 1405 Log.w(TAG, "startVoiceRecognitionByHeadset: failed request from " + fromDevice); 1406 return false; 1407 } 1408 mVoiceRecognitionTimeoutEvent = new VoiceRecognitionTimeoutEvent(fromDevice); 1409 mStateMachinesThread.getThreadHandler() 1410 .postDelayed(mVoiceRecognitionTimeoutEvent, sStartVrTimeoutMs); 1411 if (!mSystemInterface.getVoiceRecognitionWakeLock().isHeld()) { 1412 mSystemInterface.getVoiceRecognitionWakeLock().acquire(sStartVrTimeoutMs); 1413 } 1414 return true; 1415 } 1416 } 1417 1418 boolean stopVoiceRecognitionByHeadset(BluetoothDevice fromDevice) { 1419 synchronized (mStateMachines) { 1420 Log.i(TAG, "stopVoiceRecognitionByHeadset: from " + fromDevice); 1421 if (!Objects.equals(fromDevice, mActiveDevice)) { 1422 Log.w(TAG, "stopVoiceRecognitionByHeadset: " + fromDevice 1423 + " is not active, active device is " + mActiveDevice); 1424 return false; 1425 } 1426 if (!mVoiceRecognitionStarted && mVoiceRecognitionTimeoutEvent == null) { 1427 Log.w(TAG, "stopVoiceRecognitionByHeadset: voice recognition not started, device=" 1428 + fromDevice); 1429 return false; 1430 } 1431 if (mVoiceRecognitionTimeoutEvent != null) { 1432 if (mSystemInterface.getVoiceRecognitionWakeLock().isHeld()) { 1433 mSystemInterface.getVoiceRecognitionWakeLock().release(); 1434 } 1435 mStateMachinesThread.getThreadHandler() 1436 .removeCallbacks(mVoiceRecognitionTimeoutEvent); 1437 mVoiceRecognitionTimeoutEvent = null; 1438 } 1439 if (mVoiceRecognitionStarted) { 1440 if (!disconnectAudio()) { 1441 Log.w(TAG, "stopVoiceRecognitionByHeadset: failed to disconnect audio from " 1442 + fromDevice); 1443 } 1444 mVoiceRecognitionStarted = false; 1445 } 1446 if (!mSystemInterface.deactivateVoiceRecognition()) { 1447 Log.w(TAG, "stopVoiceRecognitionByHeadset: failed request from " + fromDevice); 1448 return false; 1449 } 1450 return true; 1451 } 1452 } 1453 1454 private void phoneStateChanged(int numActive, int numHeld, int callState, String number, 1455 int type, boolean isVirtualCall) { 1456 enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "Need MODIFY_PHONE_STATE permission"); 1457 synchronized (mStateMachines) { 1458 // Should stop all other audio mode in this case 1459 if ((numActive + numHeld) > 0 || callState != HeadsetHalConstants.CALL_STATE_IDLE) { 1460 if (!isVirtualCall && mVirtualCallStarted) { 1461 // stop virtual voice call if there is an incoming Telecom call update 1462 stopScoUsingVirtualVoiceCall(); 1463 } 1464 if (mVoiceRecognitionStarted) { 1465 // stop voice recognition if there is any incoming call 1466 stopVoiceRecognition(mActiveDevice); 1467 } 1468 } 1469 if (mDialingOutTimeoutEvent != null) { 1470 // Send result to state machine when dialing starts 1471 if (callState == HeadsetHalConstants.CALL_STATE_DIALING) { 1472 mStateMachinesThread.getThreadHandler() 1473 .removeCallbacks(mDialingOutTimeoutEvent); 1474 doForStateMachine(mDialingOutTimeoutEvent.mDialingOutDevice, 1475 stateMachine -> stateMachine.sendMessage( 1476 HeadsetStateMachine.DIALING_OUT_RESULT, 1 /* success */, 0, 1477 mDialingOutTimeoutEvent.mDialingOutDevice)); 1478 } else if (callState == HeadsetHalConstants.CALL_STATE_ACTIVE 1479 || callState == HeadsetHalConstants.CALL_STATE_IDLE) { 1480 // Clear the timeout event when the call is connected or disconnected 1481 if (!mStateMachinesThread.getThreadHandler() 1482 .hasCallbacks(mDialingOutTimeoutEvent)) { 1483 mDialingOutTimeoutEvent = null; 1484 } 1485 } 1486 } 1487 } 1488 mStateMachinesThread.getThreadHandler().post(() -> { 1489 boolean shouldCallAudioBeActiveBefore = shouldCallAudioBeActive(); 1490 mSystemInterface.getHeadsetPhoneState().setNumActiveCall(numActive); 1491 mSystemInterface.getHeadsetPhoneState().setNumHeldCall(numHeld); 1492 mSystemInterface.getHeadsetPhoneState().setCallState(callState); 1493 // Suspend A2DP when call about is about to become active 1494 if (callState != HeadsetHalConstants.CALL_STATE_DISCONNECTED 1495 && shouldCallAudioBeActive() && !shouldCallAudioBeActiveBefore) { 1496 mSystemInterface.getAudioManager().setParameters("A2dpSuspended=true"); 1497 } 1498 }); 1499 doForEachConnectedStateMachine( 1500 stateMachine -> stateMachine.sendMessage(HeadsetStateMachine.CALL_STATE_CHANGED, 1501 new HeadsetCallState(numActive, numHeld, callState, number, type))); 1502 mStateMachinesThread.getThreadHandler().post(() -> { 1503 if (callState == HeadsetHalConstants.CALL_STATE_IDLE 1504 && !shouldCallAudioBeActive() && !isAudioOn()) { 1505 // Resume A2DP when call ended and SCO is not connected 1506 mSystemInterface.getAudioManager().setParameters("A2dpSuspended=false"); 1507 } 1508 }); 1509 1510 } 1511 1512 private void clccResponse(int index, int direction, int status, int mode, boolean mpty, 1513 String number, int type) { 1514 enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "Need MODIFY_PHONE_STATE permission"); 1515 doForEachConnectedStateMachine( 1516 stateMachine -> stateMachine.sendMessage(HeadsetStateMachine.SEND_CCLC_RESPONSE, 1517 new HeadsetClccResponse(index, direction, status, mode, mpty, number, 1518 type))); 1519 } 1520 1521 private boolean sendVendorSpecificResultCode(BluetoothDevice device, String command, 1522 String arg) { 1523 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 1524 synchronized (mStateMachines) { 1525 final HeadsetStateMachine stateMachine = mStateMachines.get(device); 1526 if (stateMachine == null) { 1527 Log.w(TAG, "sendVendorSpecificResultCode: device " + device 1528 + " was never connected/connecting"); 1529 return false; 1530 } 1531 int connectionState = stateMachine.getConnectionState(); 1532 if (connectionState != BluetoothProfile.STATE_CONNECTED) { 1533 return false; 1534 } 1535 // Currently we support only "+ANDROID". 1536 if (!command.equals(BluetoothHeadset.VENDOR_RESULT_CODE_COMMAND_ANDROID)) { 1537 Log.w(TAG, "Disallowed unsolicited result code command: " + command); 1538 return false; 1539 } 1540 stateMachine.sendMessage(HeadsetStateMachine.SEND_VENDOR_SPECIFIC_RESULT_CODE, 1541 new HeadsetVendorSpecificResultCode(device, command, arg)); 1542 } 1543 return true; 1544 } 1545 1546 boolean isInbandRingingEnabled() { 1547 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 1548 return BluetoothHeadset.isInbandRingingSupported(this) && !SystemProperties.getBoolean( 1549 DISABLE_INBAND_RINGING_PROPERTY, false) && !mInbandRingingRuntimeDisable; 1550 } 1551 1552 /** 1553 * Called from {@link HeadsetStateMachine} in state machine thread when there is a connection 1554 * state change 1555 * 1556 * @param device remote device 1557 * @param fromState from which connection state is the change 1558 * @param toState to which connection state is the change 1559 */ 1560 @VisibleForTesting 1561 public void onConnectionStateChangedFromStateMachine(BluetoothDevice device, int fromState, 1562 int toState) { 1563 synchronized (mStateMachines) { 1564 List<BluetoothDevice> audioConnectableDevices = 1565 getDevicesMatchingConnectionStates(CONNECTING_CONNECTED_STATES); 1566 if (fromState != BluetoothProfile.STATE_CONNECTED 1567 && toState == BluetoothProfile.STATE_CONNECTED) { 1568 if (audioConnectableDevices.size() > 1) { 1569 mInbandRingingRuntimeDisable = true; 1570 doForEachConnectedStateMachine( 1571 stateMachine -> stateMachine.sendMessage(HeadsetStateMachine.SEND_BSIR, 1572 0)); 1573 } 1574 MetricsLogger.logProfileConnectionEvent(BluetoothMetricsProto.ProfileId.HEADSET); 1575 } 1576 if (fromState != BluetoothProfile.STATE_DISCONNECTED 1577 && toState == BluetoothProfile.STATE_DISCONNECTED) { 1578 if (audioConnectableDevices.size() <= 1) { 1579 mInbandRingingRuntimeDisable = false; 1580 doForEachConnectedStateMachine( 1581 stateMachine -> stateMachine.sendMessage(HeadsetStateMachine.SEND_BSIR, 1582 1)); 1583 } 1584 if (device.equals(mActiveDevice)) { 1585 setActiveDevice(null); 1586 } 1587 } 1588 } 1589 } 1590 1591 /** 1592 * Check if no audio mode is active 1593 * 1594 * @return false if virtual call, voice recognition, or Telecom call is active, true if all idle 1595 */ 1596 private boolean isAudioModeIdle() { 1597 synchronized (mStateMachines) { 1598 if (mVoiceRecognitionStarted || mVirtualCallStarted || !mSystemInterface.isCallIdle()) { 1599 Log.i(TAG, "isAudioModeIdle: not idle, mVoiceRecognitionStarted=" 1600 + mVoiceRecognitionStarted + ", mVirtualCallStarted=" + mVirtualCallStarted 1601 + ", isCallIdle=" + mSystemInterface.isCallIdle()); 1602 return false; 1603 } 1604 return true; 1605 } 1606 } 1607 1608 private boolean shouldCallAudioBeActive() { 1609 return mSystemInterface.isInCall() || (mSystemInterface.isRinging() 1610 && isInbandRingingEnabled()); 1611 } 1612 1613 /** 1614 * Only persist audio during active device switch when call audio is supposed to be active and 1615 * virtual call has not been started. Virtual call is ignored because AudioService and 1616 * applications should reconnect SCO during active device switch and forcing SCO connection 1617 * here will make AudioService think SCO is started externally instead of by one of its SCO 1618 * clients. 1619 * 1620 * @return true if call audio should be active and no virtual call is going on 1621 */ 1622 private boolean shouldPersistAudio() { 1623 return !mVirtualCallStarted && shouldCallAudioBeActive(); 1624 } 1625 1626 /** 1627 * Called from {@link HeadsetStateMachine} in state machine thread when there is a audio 1628 * connection state change 1629 * 1630 * @param device remote device 1631 * @param fromState from which audio connection state is the change 1632 * @param toState to which audio connection state is the change 1633 */ 1634 @VisibleForTesting 1635 public void onAudioStateChangedFromStateMachine(BluetoothDevice device, int fromState, 1636 int toState) { 1637 synchronized (mStateMachines) { 1638 if (toState == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { 1639 if (fromState != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { 1640 if (mActiveDevice != null && !mActiveDevice.equals(device) 1641 && shouldPersistAudio()) { 1642 if (!connectAudio(mActiveDevice)) { 1643 Log.w(TAG, "onAudioStateChangedFromStateMachine, failed to connect" 1644 + " audio to new " + "active device " + mActiveDevice 1645 + ", after " + device + " is disconnected from SCO"); 1646 } 1647 } 1648 } 1649 if (mVoiceRecognitionStarted) { 1650 if (!stopVoiceRecognitionByHeadset(device)) { 1651 Log.w(TAG, "onAudioStateChangedFromStateMachine: failed to stop voice " 1652 + "recognition"); 1653 } 1654 } 1655 if (mVirtualCallStarted) { 1656 if (!stopScoUsingVirtualVoiceCall()) { 1657 Log.w(TAG, "onAudioStateChangedFromStateMachine: failed to stop virtual " 1658 + "voice call"); 1659 } 1660 } 1661 // Unsuspend A2DP when SCO connection is gone and call state is idle 1662 if (mSystemInterface.isCallIdle()) { 1663 mSystemInterface.getAudioManager().setParameters("A2dpSuspended=false"); 1664 } 1665 } 1666 } 1667 } 1668 1669 private void broadcastActiveDevice(BluetoothDevice device) { 1670 logD("broadcastActiveDevice: " + device); 1671 Intent intent = new Intent(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED); 1672 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 1673 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT 1674 | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); 1675 sendBroadcastAsUser(intent, UserHandle.ALL, HeadsetService.BLUETOOTH_PERM); 1676 } 1677 1678 /** 1679 * Check whether it is OK to accept a headset connection from a remote device 1680 * 1681 * @param device remote device that initiates the connection 1682 * @return true if the connection is acceptable 1683 */ 1684 public boolean okToAcceptConnection(BluetoothDevice device) { 1685 // Check if this is an incoming connection in Quiet mode. 1686 if (mAdapterService.isQuietModeEnabled()) { 1687 Log.w(TAG, "okToAcceptConnection: return false as quiet mode enabled"); 1688 return false; 1689 } 1690 // Check priority and accept or reject the connection. 1691 // Note: Logic can be simplified, but keeping it this way for readability 1692 int priority = getPriority(device); 1693 int bondState = mAdapterService.getBondState(device); 1694 // If priority is undefined, it is likely that service discovery has not completed and peer 1695 // initiated the connection. Allow this connection only if the device is bonded or bonding 1696 boolean serviceDiscoveryPending = (priority == BluetoothProfile.PRIORITY_UNDEFINED) && ( 1697 bondState == BluetoothDevice.BOND_BONDING 1698 || bondState == BluetoothDevice.BOND_BONDED); 1699 // Also allow connection when device is bonded/bonding and priority is ON/AUTO_CONNECT. 1700 boolean isEnabled = (priority == BluetoothProfile.PRIORITY_ON 1701 || priority == BluetoothProfile.PRIORITY_AUTO_CONNECT) && ( 1702 bondState == BluetoothDevice.BOND_BONDED 1703 || bondState == BluetoothDevice.BOND_BONDING); 1704 if (!serviceDiscoveryPending && !isEnabled) { 1705 // Otherwise, reject the connection if no service discovery is pending and priority is 1706 // neither PRIORITY_ON nor PRIORITY_AUTO_CONNECT 1707 Log.w(TAG, 1708 "okToConnect: return false, priority=" + priority + ", bondState=" + bondState); 1709 return false; 1710 } 1711 List<BluetoothDevice> connectingConnectedDevices = 1712 getDevicesMatchingConnectionStates(CONNECTING_CONNECTED_STATES); 1713 if (connectingConnectedDevices.size() >= mMaxHeadsetConnections) { 1714 Log.w(TAG, "Maximum number of connections " + mMaxHeadsetConnections 1715 + " was reached, rejecting connection from " + device); 1716 return false; 1717 } 1718 return true; 1719 } 1720 1721 /** 1722 * Checks if SCO should be connected at current system state 1723 * 1724 * @param device device for SCO to be connected 1725 * @return true if SCO is allowed to be connected 1726 */ 1727 public boolean isScoAcceptable(BluetoothDevice device) { 1728 synchronized (mStateMachines) { 1729 if (device == null || !device.equals(mActiveDevice)) { 1730 Log.w(TAG, "isScoAcceptable: rejected SCO since " + device 1731 + " is not the current active device " + mActiveDevice); 1732 return false; 1733 } 1734 if (mForceScoAudio) { 1735 return true; 1736 } 1737 if (!mAudioRouteAllowed) { 1738 Log.w(TAG, "isScoAcceptable: rejected SCO since audio route is not allowed"); 1739 return false; 1740 } 1741 if (mVoiceRecognitionStarted || mVirtualCallStarted) { 1742 return true; 1743 } 1744 if (shouldCallAudioBeActive()) { 1745 return true; 1746 } 1747 Log.w(TAG, "isScoAcceptable: rejected SCO, inCall=" + mSystemInterface.isInCall() 1748 + ", voiceRecognition=" + mVoiceRecognitionStarted + ", ringing=" 1749 + mSystemInterface.isRinging() + ", inbandRinging=" + isInbandRingingEnabled() 1750 + ", isVirtualCallStarted=" + mVirtualCallStarted); 1751 return false; 1752 } 1753 } 1754 1755 /** 1756 * Remove state machine in {@link #mStateMachines} for a {@link BluetoothDevice} 1757 * 1758 * @param device device whose state machine is to be removed. 1759 */ 1760 void removeStateMachine(BluetoothDevice device) { 1761 synchronized (mStateMachines) { 1762 HeadsetStateMachine stateMachine = mStateMachines.get(device); 1763 if (stateMachine == null) { 1764 Log.w(TAG, "removeStateMachine(), " + device + " does not have a state machine"); 1765 return; 1766 } 1767 Log.i(TAG, "removeStateMachine(), removing state machine for device: " + device); 1768 HeadsetObjectsFactory.getInstance().destroyStateMachine(stateMachine); 1769 mStateMachines.remove(device); 1770 } 1771 } 1772 1773 private boolean isOnStateMachineThread() { 1774 final Looper myLooper = Looper.myLooper(); 1775 return myLooper != null && (mStateMachinesThread != null) && (myLooper.getThread().getId() 1776 == mStateMachinesThread.getId()); 1777 } 1778 1779 @Override 1780 public void dump(StringBuilder sb) { 1781 synchronized (mStateMachines) { 1782 super.dump(sb); 1783 ProfileService.println(sb, "mMaxHeadsetConnections: " + mMaxHeadsetConnections); 1784 ProfileService.println(sb, "DefaultMaxHeadsetConnections: " 1785 + mAdapterService.getMaxConnectedAudioDevices()); 1786 ProfileService.println(sb, "mActiveDevice: " + mActiveDevice); 1787 ProfileService.println(sb, "isInbandRingingEnabled: " + isInbandRingingEnabled()); 1788 ProfileService.println(sb, 1789 "isInbandRingingSupported: " + BluetoothHeadset.isInbandRingingSupported(this)); 1790 ProfileService.println(sb, 1791 "mInbandRingingRuntimeDisable: " + mInbandRingingRuntimeDisable); 1792 ProfileService.println(sb, "mAudioRouteAllowed: " + mAudioRouteAllowed); 1793 ProfileService.println(sb, "mVoiceRecognitionStarted: " + mVoiceRecognitionStarted); 1794 ProfileService.println(sb, 1795 "mVoiceRecognitionTimeoutEvent: " + mVoiceRecognitionTimeoutEvent); 1796 ProfileService.println(sb, "mVirtualCallStarted: " + mVirtualCallStarted); 1797 ProfileService.println(sb, "mDialingOutTimeoutEvent: " + mDialingOutTimeoutEvent); 1798 ProfileService.println(sb, "mForceScoAudio: " + mForceScoAudio); 1799 ProfileService.println(sb, "mCreated: " + mCreated); 1800 ProfileService.println(sb, "mStarted: " + mStarted); 1801 ProfileService.println(sb, 1802 "AudioManager.isBluetoothScoOn(): " + mSystemInterface.getAudioManager() 1803 .isBluetoothScoOn()); 1804 ProfileService.println(sb, "Telecom.isInCall(): " + mSystemInterface.isInCall()); 1805 ProfileService.println(sb, "Telecom.isRinging(): " + mSystemInterface.isRinging()); 1806 for (HeadsetStateMachine stateMachine : mStateMachines.values()) { 1807 ProfileService.println(sb, 1808 "==== StateMachine for " + stateMachine.getDevice() + " ===="); 1809 stateMachine.dump(sb); 1810 } 1811 } 1812 } 1813 1814 private static void logD(String message) { 1815 if (DBG) { 1816 Log.d(TAG, message); 1817 } 1818 } 1819 } 1820