HeadsetService.java revision bc5e652497d767d9a576d6b89cbacde4ed2881be
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 17package com.android.bluetooth.hfp; 18 19import static android.Manifest.permission.MODIFY_PHONE_STATE; 20 21import android.annotation.Nullable; 22import android.bluetooth.BluetoothDevice; 23import android.bluetooth.BluetoothHeadset; 24import android.bluetooth.BluetoothProfile; 25import android.bluetooth.BluetoothUuid; 26import android.bluetooth.IBluetoothHeadset; 27import android.content.BroadcastReceiver; 28import android.content.Context; 29import android.content.Intent; 30import android.content.IntentFilter; 31import android.media.AudioManager; 32import android.os.BatteryManager; 33import android.os.HandlerThread; 34import android.os.Looper; 35import android.os.ParcelUuid; 36import android.os.SystemProperties; 37import android.os.UserHandle; 38import android.provider.Settings; 39import android.util.Log; 40 41import com.android.bluetooth.BluetoothMetricsProto; 42import com.android.bluetooth.Utils; 43import com.android.bluetooth.btservice.AdapterService; 44import com.android.bluetooth.btservice.MetricsLogger; 45import com.android.bluetooth.btservice.ProfileService; 46import com.android.internal.annotations.VisibleForTesting; 47 48import java.util.ArrayList; 49import java.util.Arrays; 50import java.util.Comparator; 51import java.util.HashMap; 52import java.util.List; 53import java.util.Objects; 54 55/** 56 * Provides Bluetooth Headset and Handsfree profile, as a service in the Bluetooth application. 57 */ 58public class HeadsetService extends ProfileService { 59 private static final String TAG = "HeadsetService"; 60 private static final boolean DBG = false; 61 private static final String DISABLE_INBAND_RINGING_PROPERTY = 62 "persist.bluetooth.disableinbandringing"; 63 private static final ParcelUuid[] HEADSET_UUIDS = { 64 BluetoothUuid.HSP, BluetoothUuid.Handsfree, 65 }; 66 private static final int[] CONNECTING_CONNECTED_STATES = { 67 BluetoothProfile.STATE_CONNECTING, BluetoothProfile.STATE_CONNECTED 68 }; 69 70 private int mMaxHeadsetConnections = 1; 71 private BluetoothDevice mActiveDevice; 72 private AdapterService mAdapterService; 73 private HandlerThread mStateMachinesThread; 74 // This is also used as a lock for shared data in HeadsetService 75 private final HashMap<BluetoothDevice, HeadsetStateMachine> mStateMachines = new HashMap<>(); 76 private HeadsetNativeInterface mNativeInterface; 77 private HeadsetSystemInterface mSystemInterface; 78 private boolean mAudioRouteAllowed = true; 79 // Indicates whether SCO audio needs to be forced to open regardless ANY OTHER restrictions 80 private boolean mForceScoAudio; 81 private boolean mInbandRingingRuntimeDisable; 82 // TODO: Need better handling of voice recognition and virtual voice call events for Multi-HFP 83 private boolean mVoiceRecognitionStarted; 84 private boolean mStarted; 85 private boolean mCreated; 86 private static HeadsetService sHeadsetService; 87 88 @Override 89 public IProfileServiceBinder initBinder() { 90 return new BluetoothHeadsetBinder(this); 91 } 92 93 @Override 94 protected void create() { 95 Log.i(TAG, "create()"); 96 if (mCreated) { 97 throw new IllegalStateException("create() called twice"); 98 } 99 mCreated = true; 100 } 101 102 @Override 103 protected boolean start() { 104 Log.i(TAG, "start()"); 105 if (mStarted) { 106 throw new IllegalStateException("start() called twice"); 107 } 108 // Step 1: Get adapter service, should never be null 109 mAdapterService = Objects.requireNonNull(AdapterService.getAdapterService(), 110 "AdapterService cannot be null when HeadsetService starts"); 111 // Step 2: Start handler thread for state machines 112 mStateMachinesThread = new HandlerThread("HeadsetService.StateMachines"); 113 mStateMachinesThread.start(); 114 // Step 3: Initialize system interface 115 mSystemInterface = HeadsetObjectsFactory.getInstance().makeSystemInterface(this); 116 mSystemInterface.init(); 117 // Step 4: Initialize native interface 118 mMaxHeadsetConnections = mAdapterService.getMaxConnectedAudioDevices(); 119 mNativeInterface = HeadsetObjectsFactory.getInstance().getNativeInterface(); 120 // Add 1 to allow a pending device to be connecting or disconnecting 121 mNativeInterface.init(mMaxHeadsetConnections + 1, isInbandRingingEnabled()); 122 // Step 5: Check if state machine table is empty, crash if not 123 if (mStateMachines.size() > 0) { 124 throw new IllegalStateException( 125 "start(): mStateMachines is not empty, " + mStateMachines.size() 126 + " is already created. Was stop() called properly?"); 127 } 128 // Step 6: Setup broadcast receivers 129 IntentFilter filter = new IntentFilter(); 130 filter.addAction(Intent.ACTION_BATTERY_CHANGED); 131 filter.addAction(AudioManager.VOLUME_CHANGED_ACTION); 132 filter.addAction(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY); 133 filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED); 134 registerReceiver(mHeadsetReceiver, filter); 135 // Step 7: Mark service as started 136 setHeadsetService(this); 137 mStarted = true; 138 return true; 139 } 140 141 @Override 142 protected boolean stop() { 143 Log.i(TAG, "stop()"); 144 if (!mStarted) { 145 Log.w(TAG, "stop() called before start()"); 146 // Still return true because it is considered "stopped" and doesn't have any functional 147 // impact on the user 148 return true; 149 } 150 // Step 7: Mark service as stopped 151 mStarted = false; 152 setHeadsetService(null); 153 // Reset active device to null 154 mActiveDevice = null; 155 mInbandRingingRuntimeDisable = false; 156 mForceScoAudio = false; 157 mAudioRouteAllowed = true; 158 mMaxHeadsetConnections = 1; 159 mVoiceRecognitionStarted = false; 160 // Step 6: Tear down broadcast receivers 161 unregisterReceiver(mHeadsetReceiver); 162 // Step 5: Destroy state machines 163 synchronized (mStateMachines) { 164 for (HeadsetStateMachine stateMachine : mStateMachines.values()) { 165 HeadsetObjectsFactory.getInstance().destroyStateMachine(stateMachine); 166 } 167 mStateMachines.clear(); 168 } 169 // Step 4: Destroy native interface 170 mNativeInterface.cleanup(); 171 // Step 3: Destroy system interface 172 mSystemInterface.stop(); 173 // Step 2: Stop handler thread 174 mStateMachinesThread.quitSafely(); 175 mStateMachinesThread = null; 176 // Step 1: Clear 177 mAdapterService = null; 178 return true; 179 } 180 181 @Override 182 protected void cleanup() { 183 Log.i(TAG, "cleanup"); 184 if (!mCreated) { 185 Log.w(TAG, "cleanup() called before create()"); 186 } 187 mCreated = false; 188 } 189 190 /** 191 * Checks if this service object is able to accept binder calls 192 * 193 * @return True if the object can accept binder calls, False otherwise 194 */ 195 public boolean isAlive() { 196 return isAvailable() && mCreated && mStarted; 197 } 198 199 /** 200 * Get the {@link Looper} for the state machine thread. This is used in testing and helper 201 * objects 202 * 203 * @return {@link Looper} for the state machine thread 204 */ 205 @VisibleForTesting 206 public Looper getStateMachinesThreadLooper() { 207 return mStateMachinesThread.getLooper(); 208 } 209 210 interface StateMachineTask { 211 void execute(HeadsetStateMachine stateMachine); 212 } 213 214 private void doForEachConnectedStateMachine(StateMachineTask task) { 215 synchronized (mStateMachines) { 216 for (BluetoothDevice device : getConnectedDevices()) { 217 task.execute(mStateMachines.get(device)); 218 } 219 } 220 } 221 222 void onDeviceStateChanged(HeadsetDeviceState deviceState) { 223 doForEachConnectedStateMachine( 224 stateMachine -> stateMachine.sendMessage(HeadsetStateMachine.DEVICE_STATE_CHANGED, 225 deviceState)); 226 } 227 228 /** 229 * Handle messages from native (JNI) to Java. This needs to be synchronized to avoid posting 230 * messages to state machine before start() is done 231 * 232 * @param stackEvent event from native stack 233 */ 234 void messageFromNative(HeadsetStackEvent stackEvent) { 235 Objects.requireNonNull(stackEvent.device, 236 "Device should never be null, event: " + stackEvent); 237 synchronized (mStateMachines) { 238 HeadsetStateMachine stateMachine = mStateMachines.get(stackEvent.device); 239 if (stackEvent.type == HeadsetStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED) { 240 switch (stackEvent.valueInt) { 241 case HeadsetHalConstants.CONNECTION_STATE_CONNECTED: 242 case HeadsetHalConstants.CONNECTION_STATE_CONNECTING: { 243 // Create new state machine if none is found 244 if (stateMachine == null) { 245 stateMachine = HeadsetObjectsFactory.getInstance() 246 .makeStateMachine(stackEvent.device, 247 mStateMachinesThread.getLooper(), this, mAdapterService, 248 mNativeInterface, mSystemInterface); 249 mStateMachines.put(stackEvent.device, stateMachine); 250 } 251 break; 252 } 253 } 254 } 255 if (stateMachine == null) { 256 throw new IllegalStateException( 257 "State machine not found for stack event: " + stackEvent); 258 } 259 stateMachine.sendMessage(HeadsetStateMachine.STACK_EVENT, stackEvent); 260 } 261 } 262 263 private final BroadcastReceiver mHeadsetReceiver = new BroadcastReceiver() { 264 @Override 265 public void onReceive(Context context, Intent intent) { 266 String action = intent.getAction(); 267 if (action == null) { 268 Log.w(TAG, "mHeadsetReceiver, action is null"); 269 return; 270 } 271 switch (action) { 272 case Intent.ACTION_BATTERY_CHANGED: { 273 int batteryLevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1); 274 int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1); 275 if (batteryLevel < 0 || scale <= 0) { 276 Log.e(TAG, "Bad Battery Changed intent: batteryLevel=" + batteryLevel 277 + ", scale=" + scale); 278 return; 279 } 280 batteryLevel = batteryLevel * 5 / scale; 281 mSystemInterface.getHeadsetPhoneState().setCindBatteryCharge(batteryLevel); 282 break; 283 } 284 case AudioManager.VOLUME_CHANGED_ACTION: { 285 int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1); 286 if (streamType == AudioManager.STREAM_BLUETOOTH_SCO) { 287 doForEachConnectedStateMachine(stateMachine -> stateMachine.sendMessage( 288 HeadsetStateMachine.INTENT_SCO_VOLUME_CHANGED, intent)); 289 } 290 break; 291 } 292 case BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY: { 293 int requestType = intent.getIntExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE, 294 BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS); 295 BluetoothDevice device = 296 intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 297 logD("Received BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY, device=" + device 298 + ", type=" + requestType); 299 if (requestType == BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS) { 300 synchronized (mStateMachines) { 301 final HeadsetStateMachine stateMachine = mStateMachines.get(device); 302 if (stateMachine == null) { 303 Log.wtfStack(TAG, "Cannot find state machine for " + device); 304 return; 305 } 306 stateMachine.sendMessage( 307 HeadsetStateMachine.INTENT_CONNECTION_ACCESS_REPLY, intent); 308 } 309 } 310 break; 311 } 312 case BluetoothDevice.ACTION_BOND_STATE_CHANGED: { 313 int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, 314 BluetoothDevice.ERROR); 315 BluetoothDevice device = Objects.requireNonNull( 316 intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE), 317 "ACTION_BOND_STATE_CHANGED with no EXTRA_DEVICE"); 318 logD("Bond state changed for device: " + device + " state: " + state); 319 if (state != BluetoothDevice.BOND_NONE) { 320 break; 321 } 322 synchronized (mStateMachines) { 323 HeadsetStateMachine stateMachine = mStateMachines.get(device); 324 if (stateMachine == null) { 325 break; 326 } 327 if (stateMachine.getConnectionState() 328 != BluetoothProfile.STATE_DISCONNECTED) { 329 break; 330 } 331 removeStateMachine(device); 332 } 333 break; 334 } 335 default: 336 Log.w(TAG, "Unknown action " + action); 337 } 338 } 339 }; 340 341 /** 342 * Handlers for incoming service calls 343 */ 344 private static class BluetoothHeadsetBinder extends IBluetoothHeadset.Stub 345 implements IProfileServiceBinder { 346 private volatile HeadsetService mService; 347 348 BluetoothHeadsetBinder(HeadsetService svc) { 349 mService = svc; 350 } 351 352 @Override 353 public void cleanup() { 354 mService = null; 355 } 356 357 private HeadsetService getService() { 358 final HeadsetService service = mService; 359 if (!Utils.checkCallerAllowManagedProfiles(service)) { 360 Log.w(TAG, "Headset call not allowed for non-active user"); 361 return null; 362 } 363 if (service == null) { 364 Log.w(TAG, "Service is null"); 365 return null; 366 } 367 if (!service.isAlive()) { 368 Log.w(TAG, "Service is not alive"); 369 return null; 370 } 371 return service; 372 } 373 374 @Override 375 public boolean connect(BluetoothDevice device) { 376 HeadsetService service = getService(); 377 if (service == null) { 378 return false; 379 } 380 return service.connect(device); 381 } 382 383 @Override 384 public boolean disconnect(BluetoothDevice device) { 385 HeadsetService service = getService(); 386 if (service == null) { 387 return false; 388 } 389 logD("disconnect in HeadsetService"); 390 return service.disconnect(device); 391 } 392 393 @Override 394 public List<BluetoothDevice> getConnectedDevices() { 395 HeadsetService service = getService(); 396 if (service == null) { 397 return new ArrayList<BluetoothDevice>(0); 398 } 399 return service.getConnectedDevices(); 400 } 401 402 @Override 403 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 404 HeadsetService service = getService(); 405 if (service == null) { 406 return new ArrayList<BluetoothDevice>(0); 407 } 408 return service.getDevicesMatchingConnectionStates(states); 409 } 410 411 @Override 412 public int getConnectionState(BluetoothDevice device) { 413 HeadsetService service = getService(); 414 if (service == null) { 415 return BluetoothProfile.STATE_DISCONNECTED; 416 } 417 return service.getConnectionState(device); 418 } 419 420 @Override 421 public boolean setPriority(BluetoothDevice device, int priority) { 422 HeadsetService service = getService(); 423 if (service == null) { 424 return false; 425 } 426 return service.setPriority(device, priority); 427 } 428 429 @Override 430 public int getPriority(BluetoothDevice device) { 431 HeadsetService service = getService(); 432 if (service == null) { 433 return BluetoothProfile.PRIORITY_UNDEFINED; 434 } 435 return service.getPriority(device); 436 } 437 438 @Override 439 public boolean startVoiceRecognition(BluetoothDevice device) { 440 HeadsetService service = getService(); 441 if (service == null) { 442 return false; 443 } 444 return service.startVoiceRecognition(device); 445 } 446 447 @Override 448 public boolean stopVoiceRecognition(BluetoothDevice device) { 449 HeadsetService service = getService(); 450 if (service == null) { 451 return false; 452 } 453 return service.stopVoiceRecognition(device); 454 } 455 456 @Override 457 public boolean isAudioOn() { 458 HeadsetService service = getService(); 459 if (service == null) { 460 return false; 461 } 462 return service.isAudioOn(); 463 } 464 465 @Override 466 public boolean isAudioConnected(BluetoothDevice device) { 467 HeadsetService service = getService(); 468 if (service == null) { 469 return false; 470 } 471 return service.isAudioConnected(device); 472 } 473 474 @Override 475 public int getAudioState(BluetoothDevice device) { 476 HeadsetService service = getService(); 477 if (service == null) { 478 return BluetoothHeadset.STATE_AUDIO_DISCONNECTED; 479 } 480 return service.getAudioState(device); 481 } 482 483 @Override 484 public boolean connectAudio() { 485 HeadsetService service = getService(); 486 if (service == null) { 487 return false; 488 } 489 return service.connectAudio(); 490 } 491 492 @Override 493 public boolean disconnectAudio() { 494 HeadsetService service = getService(); 495 if (service == null) { 496 return false; 497 } 498 return service.disconnectAudio(); 499 } 500 501 @Override 502 public void setAudioRouteAllowed(boolean allowed) { 503 HeadsetService service = getService(); 504 if (service == null) { 505 return; 506 } 507 service.setAudioRouteAllowed(allowed); 508 } 509 510 @Override 511 public boolean getAudioRouteAllowed() { 512 HeadsetService service = getService(); 513 if (service != null) { 514 return service.getAudioRouteAllowed(); 515 } 516 return false; 517 } 518 519 @Override 520 public void setForceScoAudio(boolean forced) { 521 HeadsetService service = getService(); 522 if (service == null) { 523 return; 524 } 525 service.setForceScoAudio(forced); 526 } 527 528 @Override 529 public boolean startScoUsingVirtualVoiceCall(BluetoothDevice device) { 530 HeadsetService service = getService(); 531 if (service == null) { 532 return false; 533 } 534 return service.startScoUsingVirtualVoiceCall(device); 535 } 536 537 @Override 538 public boolean stopScoUsingVirtualVoiceCall(BluetoothDevice device) { 539 HeadsetService service = getService(); 540 if (service == null) { 541 return false; 542 } 543 return service.stopScoUsingVirtualVoiceCall(device); 544 } 545 546 @Override 547 public void phoneStateChanged(int numActive, int numHeld, int callState, String number, 548 int type) { 549 HeadsetService service = getService(); 550 if (service == null) { 551 return; 552 } 553 service.phoneStateChanged(numActive, numHeld, callState, number, type); 554 } 555 556 @Override 557 public void clccResponse(int index, int direction, int status, int mode, boolean mpty, 558 String number, int type) { 559 HeadsetService service = getService(); 560 if (service == null) { 561 return; 562 } 563 service.clccResponse(index, direction, status, mode, mpty, number, type); 564 } 565 566 @Override 567 public boolean sendVendorSpecificResultCode(BluetoothDevice device, String command, 568 String arg) { 569 HeadsetService service = getService(); 570 if (service == null) { 571 return false; 572 } 573 return service.sendVendorSpecificResultCode(device, command, arg); 574 } 575 576 @Override 577 public boolean setActiveDevice(BluetoothDevice device) { 578 HeadsetService service = getService(); 579 if (service == null) { 580 return false; 581 } 582 return service.setActiveDevice(device); 583 } 584 585 @Override 586 public BluetoothDevice getActiveDevice() { 587 HeadsetService service = getService(); 588 if (service == null) { 589 return null; 590 } 591 return service.getActiveDevice(); 592 } 593 594 @Override 595 public boolean isInbandRingingEnabled() { 596 HeadsetService service = getService(); 597 if (service == null) { 598 return false; 599 } 600 return service.isInbandRingingEnabled(); 601 } 602 } 603 604 // API methods 605 public static synchronized HeadsetService getHeadsetService() { 606 if (sHeadsetService == null) { 607 Log.w(TAG, "getHeadsetService(): service is NULL"); 608 return null; 609 } 610 if (!sHeadsetService.isAvailable()) { 611 Log.w(TAG, "getHeadsetService(): service is not available"); 612 return null; 613 } 614 logD("getHeadsetService(): returning " + sHeadsetService); 615 return sHeadsetService; 616 } 617 618 private static synchronized void setHeadsetService(HeadsetService instance) { 619 logD("setHeadsetService(): set to: " + instance); 620 sHeadsetService = instance; 621 } 622 623 public boolean connect(BluetoothDevice device) { 624 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission"); 625 if (getPriority(device) == BluetoothProfile.PRIORITY_OFF) { 626 Log.w(TAG, "connect: PRIORITY_OFF, device=" + device); 627 return false; 628 } 629 ParcelUuid[] featureUuids = mAdapterService.getRemoteUuids(device); 630 if (!BluetoothUuid.containsAnyUuid(featureUuids, HEADSET_UUIDS)) { 631 Log.e(TAG, "connect: Cannot connect to " + device + ": no headset UUID"); 632 return false; 633 } 634 synchronized (mStateMachines) { 635 Log.i(TAG, "connect: device=" + device); 636 HeadsetStateMachine stateMachine = mStateMachines.get(device); 637 if (stateMachine == null) { 638 stateMachine = HeadsetObjectsFactory.getInstance() 639 .makeStateMachine(device, mStateMachinesThread.getLooper(), this, 640 mAdapterService, mNativeInterface, mSystemInterface); 641 mStateMachines.put(device, stateMachine); 642 } 643 int connectionState = stateMachine.getConnectionState(); 644 if (connectionState == BluetoothProfile.STATE_CONNECTED 645 || connectionState == BluetoothProfile.STATE_CONNECTING) { 646 Log.w(TAG, "connect: device " + device 647 + " is already connected/connecting, connectionState=" + connectionState); 648 return false; 649 } 650 List<BluetoothDevice> connectingConnectedDevices = 651 getDevicesMatchingConnectionStates(CONNECTING_CONNECTED_STATES); 652 boolean disconnectExisting = false; 653 if (connectingConnectedDevices.size() >= mMaxHeadsetConnections) { 654 // When there is maximum one device, we automatically disconnect the current one 655 if (mMaxHeadsetConnections == 1) { 656 disconnectExisting = true; 657 } else { 658 Log.w(TAG, "Max connection has reached, rejecting connection to " + device); 659 return false; 660 } 661 } 662 if (disconnectExisting) { 663 for (BluetoothDevice connectingConnectedDevice : connectingConnectedDevices) { 664 disconnect(connectingConnectedDevice); 665 } 666 setActiveDevice(null); 667 } 668 stateMachine.sendMessage(HeadsetStateMachine.CONNECT, device); 669 } 670 return true; 671 } 672 673 boolean disconnect(BluetoothDevice device) { 674 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); 675 synchronized (mStateMachines) { 676 Log.i(TAG, "disconnect: device=" + device); 677 HeadsetStateMachine stateMachine = mStateMachines.get(device); 678 if (stateMachine == null) { 679 Log.w(TAG, "disconnect: device " + device + " not ever connected/connecting"); 680 return false; 681 } 682 int connectionState = stateMachine.getConnectionState(); 683 if (connectionState != BluetoothProfile.STATE_CONNECTED 684 && connectionState != BluetoothProfile.STATE_CONNECTING) { 685 Log.w(TAG, "disconnect: device " + device 686 + " not connected/connecting, connectionState=" + connectionState); 687 return false; 688 } 689 stateMachine.sendMessage(HeadsetStateMachine.DISCONNECT, device); 690 } 691 return true; 692 } 693 694 public List<BluetoothDevice> getConnectedDevices() { 695 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 696 ArrayList<BluetoothDevice> devices = new ArrayList<>(); 697 synchronized (mStateMachines) { 698 for (HeadsetStateMachine stateMachine : mStateMachines.values()) { 699 if (stateMachine.getConnectionState() == BluetoothProfile.STATE_CONNECTED) { 700 devices.add(stateMachine.getDevice()); 701 } 702 } 703 } 704 return devices; 705 } 706 707 /** 708 * Same as the API method {@link BluetoothHeadset#getDevicesMatchingConnectionStates(int[])} 709 * 710 * @param states an array of states from {@link BluetoothProfile} 711 * @return a list of devices matching the array of connection states 712 */ 713 @VisibleForTesting 714 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 715 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 716 ArrayList<BluetoothDevice> devices = new ArrayList<>(); 717 if (states == null) { 718 return devices; 719 } 720 final BluetoothDevice[] bondedDevices = mAdapterService.getBondedDevices(); 721 if (bondedDevices == null) { 722 return devices; 723 } 724 synchronized (mStateMachines) { 725 for (BluetoothDevice device : bondedDevices) { 726 final ParcelUuid[] featureUuids = mAdapterService.getRemoteUuids(device); 727 if (!BluetoothUuid.containsAnyUuid(featureUuids, HEADSET_UUIDS)) { 728 continue; 729 } 730 int connectionState = getConnectionState(device); 731 for (int state : states) { 732 if (connectionState == state) { 733 devices.add(device); 734 break; 735 } 736 } 737 } 738 } 739 return devices; 740 } 741 742 public int getConnectionState(BluetoothDevice device) { 743 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 744 synchronized (mStateMachines) { 745 final HeadsetStateMachine stateMachine = mStateMachines.get(device); 746 if (stateMachine == null) { 747 return BluetoothProfile.STATE_DISCONNECTED; 748 } 749 return stateMachine.getConnectionState(); 750 } 751 } 752 753 public boolean setPriority(BluetoothDevice device, int priority) { 754 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); 755 Settings.Global.putInt(getContentResolver(), 756 Settings.Global.getBluetoothHeadsetPriorityKey(device.getAddress()), priority); 757 logD("Saved priority " + device + " = " + priority); 758 return true; 759 } 760 761 public int getPriority(BluetoothDevice device) { 762 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 763 return Settings.Global.getInt(getContentResolver(), 764 Settings.Global.getBluetoothHeadsetPriorityKey(device.getAddress()), 765 BluetoothProfile.PRIORITY_UNDEFINED); 766 } 767 768 boolean startVoiceRecognition(BluetoothDevice device) { 769 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 770 synchronized (mStateMachines) { 771 final HeadsetStateMachine stateMachine = mStateMachines.get(device); 772 if (stateMachine == null) { 773 return false; 774 } 775 int connectionState = stateMachine.getConnectionState(); 776 if (connectionState != BluetoothProfile.STATE_CONNECTED 777 && connectionState != BluetoothProfile.STATE_CONNECTING) { 778 return false; 779 } 780 mVoiceRecognitionStarted = true; 781 stateMachine.sendMessage(HeadsetStateMachine.VOICE_RECOGNITION_START, device); 782 } 783 return true; 784 } 785 786 boolean stopVoiceRecognition(BluetoothDevice device) { 787 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 788 synchronized (mStateMachines) { 789 final HeadsetStateMachine stateMachine = mStateMachines.get(device); 790 if (stateMachine == null) { 791 return false; 792 } 793 // It seem that we really need to check the AudioOn state. 794 // But since we allow startVoiceRecognition in STATE_CONNECTED and 795 // STATE_CONNECTING state, we do these 2 in this method 796 int connectionState = stateMachine.getConnectionState(); 797 if (connectionState != BluetoothProfile.STATE_CONNECTED 798 && connectionState != BluetoothProfile.STATE_CONNECTING) { 799 return false; 800 } 801 mVoiceRecognitionStarted = false; 802 stateMachine.sendMessage(HeadsetStateMachine.VOICE_RECOGNITION_STOP, device); 803 } 804 return true; 805 } 806 807 boolean isAudioOn() { 808 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 809 return getNonIdleAudioDevices().size() > 0; 810 } 811 812 boolean isAudioConnected(BluetoothDevice device) { 813 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 814 synchronized (mStateMachines) { 815 final HeadsetStateMachine stateMachine = mStateMachines.get(device); 816 if (stateMachine == null) { 817 return false; 818 } 819 return stateMachine.getAudioState() == BluetoothHeadset.STATE_AUDIO_CONNECTED; 820 } 821 } 822 823 int getAudioState(BluetoothDevice device) { 824 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 825 synchronized (mStateMachines) { 826 final HeadsetStateMachine stateMachine = mStateMachines.get(device); 827 if (stateMachine == null) { 828 return BluetoothHeadset.STATE_AUDIO_DISCONNECTED; 829 } 830 return stateMachine.getAudioState(); 831 } 832 } 833 834 public void setAudioRouteAllowed(boolean allowed) { 835 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); 836 mAudioRouteAllowed = allowed; 837 mNativeInterface.setScoAllowed(allowed); 838 } 839 840 public boolean getAudioRouteAllowed() { 841 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 842 return mAudioRouteAllowed; 843 } 844 845 public void setForceScoAudio(boolean forced) { 846 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); 847 mForceScoAudio = forced; 848 } 849 850 @VisibleForTesting 851 public boolean getForceScoAudio() { 852 return mForceScoAudio; 853 } 854 855 /** 856 * Get first available device for SCO audio 857 * 858 * @return first connected headset device 859 */ 860 @VisibleForTesting 861 @Nullable 862 public BluetoothDevice getFirstConnectedAudioDevice() { 863 ArrayList<HeadsetStateMachine> stateMachines = new ArrayList<>(); 864 synchronized (mStateMachines) { 865 List<BluetoothDevice> availableDevices = 866 getDevicesMatchingConnectionStates(CONNECTING_CONNECTED_STATES); 867 for (BluetoothDevice device : availableDevices) { 868 final HeadsetStateMachine stateMachine = mStateMachines.get(device); 869 if (stateMachine == null) { 870 continue; 871 } 872 stateMachines.add(stateMachine); 873 } 874 } 875 stateMachines.sort(Comparator.comparingLong(HeadsetStateMachine::getConnectingTimestampMs)); 876 if (stateMachines.size() > 0) { 877 return stateMachines.get(0).getDevice(); 878 } 879 return null; 880 } 881 882 /** 883 * Set the active device. 884 * 885 * @param device the active device 886 * @return true on success, otherwise false 887 */ 888 public boolean setActiveDevice(BluetoothDevice device) { 889 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); 890 synchronized (mStateMachines) { 891 logD("setActiveDevice: " + device); 892 if (device == null) { 893 // Clear the active device 894 if (getAudioState(mActiveDevice) != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { 895 if (!disconnectAudio(mActiveDevice)) { 896 Log.e(TAG, 897 "setActiveDevice: fail to disconnectAudio from " + mActiveDevice); 898 return false; 899 } 900 } 901 mActiveDevice = null; 902 broadcastActiveDevice(null); 903 return true; 904 } 905 if (device.equals(mActiveDevice)) { 906 Log.w(TAG, "setActiveDevice: device " + device + " is already active"); 907 return true; 908 } 909 if (getConnectionState(device) != BluetoothProfile.STATE_CONNECTED) { 910 Log.e(TAG, "setActiveDevice: Cannot set " + device 911 + " as active, device is not connected"); 912 return false; 913 } 914 if (!mNativeInterface.setActiveDevice(device)) { 915 Log.e(TAG, "setActiveDevice: Cannot set " + device + " as active in native layer"); 916 return false; 917 } 918 BluetoothDevice previousActiveDevice = mActiveDevice; 919 mActiveDevice = device; 920 if (getAudioState(previousActiveDevice) != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { 921 mSystemInterface.getAudioManager().setBluetoothScoOn(false); 922 if (!disconnectAudio(previousActiveDevice)) { 923 Log.e(TAG, "setActiveDevice: fail to disconnectAudio from " 924 + previousActiveDevice); 925 mActiveDevice = previousActiveDevice; 926 mNativeInterface.setActiveDevice(previousActiveDevice); 927 return false; 928 } 929 broadcastActiveDevice(mActiveDevice); 930 } else if (mSystemInterface.isInCall() || (mSystemInterface.isRinging() 931 && isInbandRingingEnabled()) || mVoiceRecognitionStarted) { 932 broadcastActiveDevice(mActiveDevice); 933 if (!connectAudio(mActiveDevice)) { 934 Log.e(TAG, "setActiveDevice: fail to connectAudio to " + mActiveDevice); 935 mActiveDevice = previousActiveDevice; 936 mNativeInterface.setActiveDevice(previousActiveDevice); 937 return false; 938 } 939 } else { 940 broadcastActiveDevice(mActiveDevice); 941 } 942 } 943 return true; 944 } 945 946 /** 947 * Get the active device. 948 * 949 * @return the active device or null if no device is active 950 */ 951 public BluetoothDevice getActiveDevice() { 952 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 953 synchronized (mStateMachines) { 954 return mActiveDevice; 955 } 956 } 957 958 boolean connectAudio() { 959 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); 960 synchronized (mStateMachines) { 961 BluetoothDevice device = mActiveDevice; 962 if (device == null) { 963 Log.w(TAG, "connectAudio: no active device is selected"); 964 return false; 965 } 966 return connectAudio(device); 967 } 968 } 969 970 boolean connectAudio(BluetoothDevice device) { 971 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); 972 if (device == null) { 973 Log.w(TAG, "connectAudio: device is null"); 974 return false; 975 } 976 if (!device.equals(mActiveDevice)) { 977 Log.w(TAG, "connectAudio: device is not active, active device is " + mActiveDevice); 978 return false; 979 } 980 synchronized (mStateMachines) { 981 Log.i(TAG, "connectAudio, device=" + device); 982 final HeadsetStateMachine stateMachine = mStateMachines.get(device); 983 if (stateMachine == null) { 984 Log.w(TAG, "connectAudio: device " + device + " was never connected/connecting"); 985 return false; 986 } 987 if (stateMachine.getConnectionState() != BluetoothProfile.STATE_CONNECTED) { 988 Log.w(TAG, "connectAudio: profile not connected"); 989 return false; 990 } 991 if (stateMachine.getAudioState() != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { 992 logD("connectAudio: audio is not idle for device " + device); 993 return true; 994 } 995 if (isAudioOn()) { 996 Log.w(TAG, "connectAudio: audio is not idle, current audio devices are " 997 + Arrays.toString(getNonIdleAudioDevices().toArray())); 998 return false; 999 } 1000 stateMachine.sendMessage(HeadsetStateMachine.CONNECT_AUDIO, device); 1001 } 1002 1003 return true; 1004 } 1005 1006 private List<BluetoothDevice> getNonIdleAudioDevices() { 1007 ArrayList<BluetoothDevice> devices = new ArrayList<>(); 1008 synchronized (mStateMachines) { 1009 for (HeadsetStateMachine stateMachine : mStateMachines.values()) { 1010 if (stateMachine.getAudioState() != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { 1011 devices.add(stateMachine.getDevice()); 1012 } 1013 } 1014 } 1015 return devices; 1016 } 1017 1018 boolean disconnectAudio() { 1019 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); 1020 boolean result = false; 1021 synchronized (mStateMachines) { 1022 for (BluetoothDevice device : getNonIdleAudioDevices()) { 1023 if (!disconnectAudio(device)) { 1024 Log.e(TAG, "disconnectAudio() from " + device + " failed"); 1025 } else { 1026 result = true; 1027 } 1028 } 1029 } 1030 if (!result) { 1031 logD("disconnectAudio() no active audio connection"); 1032 } 1033 return result; 1034 } 1035 1036 boolean disconnectAudio(BluetoothDevice device) { 1037 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); 1038 synchronized (mStateMachines) { 1039 Log.i(TAG, "disconnectAudio, device=" + device); 1040 final HeadsetStateMachine stateMachine = mStateMachines.get(device); 1041 if (stateMachine == null) { 1042 Log.w(TAG, "disconnectAudio: device " + device + " was never connected/connecting"); 1043 return false; 1044 } 1045 if (stateMachine.getAudioState() == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { 1046 Log.w(TAG, "disconnectAudio, audio is already disconnected for " + device); 1047 return false; 1048 } 1049 stateMachine.sendMessage(HeadsetStateMachine.DISCONNECT_AUDIO, device); 1050 } 1051 return true; 1052 } 1053 1054 boolean startScoUsingVirtualVoiceCall(BluetoothDevice device) { 1055 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); 1056 synchronized (mStateMachines) { 1057 final HeadsetStateMachine stateMachine = mStateMachines.get(device); 1058 if (stateMachine == null) { 1059 Log.w(TAG, "startScoUsingVirtualVoiceCall: device " + device 1060 + " was never connected/connecting"); 1061 return false; 1062 } 1063 stateMachine.sendMessage(HeadsetStateMachine.VIRTUAL_CALL_START, device); 1064 } 1065 return true; 1066 } 1067 1068 boolean stopScoUsingVirtualVoiceCall(BluetoothDevice device) { 1069 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); 1070 synchronized (mStateMachines) { 1071 final HeadsetStateMachine stateMachine = mStateMachines.get(device); 1072 if (stateMachine == null) { 1073 Log.w(TAG, "stopScoUsingVirtualVoiceCall: device " + device 1074 + " was never connected/connecting"); 1075 return false; 1076 } 1077 int connectionState = stateMachine.getConnectionState(); 1078 if (connectionState != BluetoothProfile.STATE_CONNECTED 1079 && connectionState != BluetoothProfile.STATE_CONNECTING) { 1080 Log.w(TAG, "stopScoUsingVirtualVoiceCall: device " + device 1081 + " is neither connected/connecting"); 1082 return false; 1083 } 1084 stateMachine.sendMessage(HeadsetStateMachine.VIRTUAL_CALL_STOP, device); 1085 } 1086 return true; 1087 } 1088 1089 private void phoneStateChanged(int numActive, int numHeld, int callState, String number, 1090 int type) { 1091 enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, null); 1092 doForEachConnectedStateMachine( 1093 stateMachine -> stateMachine.sendMessage(HeadsetStateMachine.CALL_STATE_CHANGED, 1094 new HeadsetCallState(numActive, numHeld, callState, number, type))); 1095 } 1096 1097 private void clccResponse(int index, int direction, int status, int mode, boolean mpty, 1098 String number, int type) { 1099 enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, null); 1100 doForEachConnectedStateMachine( 1101 stateMachine -> stateMachine.sendMessage(HeadsetStateMachine.SEND_CCLC_RESPONSE, 1102 new HeadsetClccResponse(index, direction, status, mode, mpty, number, 1103 type))); 1104 } 1105 1106 private boolean sendVendorSpecificResultCode(BluetoothDevice device, String command, 1107 String arg) { 1108 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 1109 synchronized (mStateMachines) { 1110 final HeadsetStateMachine stateMachine = mStateMachines.get(device); 1111 if (stateMachine == null) { 1112 Log.w(TAG, "sendVendorSpecificResultCode: device " + device 1113 + " was never connected/connecting"); 1114 return false; 1115 } 1116 int connectionState = stateMachine.getConnectionState(); 1117 if (connectionState != BluetoothProfile.STATE_CONNECTED) { 1118 return false; 1119 } 1120 // Currently we support only "+ANDROID". 1121 if (!command.equals(BluetoothHeadset.VENDOR_RESULT_CODE_COMMAND_ANDROID)) { 1122 Log.w(TAG, "Disallowed unsolicited result code command: " + command); 1123 return false; 1124 } 1125 stateMachine.sendMessage(HeadsetStateMachine.SEND_VENDOR_SPECIFIC_RESULT_CODE, 1126 new HeadsetVendorSpecificResultCode(device, command, arg)); 1127 } 1128 return true; 1129 } 1130 1131 boolean isInbandRingingEnabled() { 1132 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 1133 return BluetoothHeadset.isInbandRingingSupported(this) && !SystemProperties.getBoolean( 1134 DISABLE_INBAND_RINGING_PROPERTY, false) && !mInbandRingingRuntimeDisable; 1135 } 1136 1137 /** 1138 * Called from {@link HeadsetStateMachine} in state machine thread when there is a connection 1139 * state change 1140 * 1141 * @param device remote device 1142 * @param fromState from which connection state is the change 1143 * @param toState to which connection state is the change 1144 */ 1145 @VisibleForTesting 1146 public void onConnectionStateChangedFromStateMachine(BluetoothDevice device, int fromState, 1147 int toState) { 1148 synchronized (mStateMachines) { 1149 List<BluetoothDevice> audioConnectableDevices = 1150 getDevicesMatchingConnectionStates(CONNECTING_CONNECTED_STATES); 1151 if (fromState != BluetoothProfile.STATE_CONNECTED 1152 && toState == BluetoothProfile.STATE_CONNECTED) { 1153 if (audioConnectableDevices.size() > 1) { 1154 mInbandRingingRuntimeDisable = true; 1155 doForEachConnectedStateMachine( 1156 stateMachine -> stateMachine.sendMessage(HeadsetStateMachine.SEND_BSIR, 1157 0)); 1158 } 1159 MetricsLogger.logProfileConnectionEvent(BluetoothMetricsProto.ProfileId.HEADSET); 1160 } 1161 if (fromState == BluetoothProfile.STATE_CONNECTED 1162 && toState != BluetoothProfile.STATE_CONNECTED) { 1163 if (audioConnectableDevices.size() <= 1) { 1164 mInbandRingingRuntimeDisable = false; 1165 doForEachConnectedStateMachine( 1166 stateMachine -> stateMachine.sendMessage(HeadsetStateMachine.SEND_BSIR, 1167 1)); 1168 } 1169 if (device.equals(mActiveDevice)) { 1170 setActiveDevice(null); 1171 } 1172 } 1173 } 1174 } 1175 1176 /** 1177 * Called from {@link HeadsetStateMachine} in state machine thread when there is a audio 1178 * connection state change 1179 * 1180 * @param device remote device 1181 * @param fromState from which audio connection state is the change 1182 * @param toState to which audio connection state is the change 1183 */ 1184 @VisibleForTesting 1185 public void onAudioStateChangedFromStateMachine(BluetoothDevice device, int fromState, 1186 int toState) { 1187 synchronized (mStateMachines) { 1188 if (fromState != BluetoothHeadset.STATE_AUDIO_CONNECTED 1189 && toState == BluetoothHeadset.STATE_AUDIO_CONNECTED) { 1190 // Do nothing 1191 } 1192 if (fromState != BluetoothHeadset.STATE_AUDIO_DISCONNECTED 1193 && toState == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { 1194 if (mActiveDevice != null && !mActiveDevice.equals(device)) { 1195 if (!connectAudio(mActiveDevice)) { 1196 Log.w(TAG, "onAudioStateChangedFromStateMachine, failed to connect to new " 1197 + "active device " + mActiveDevice + ", after " + device 1198 + " is disconnected"); 1199 setActiveDevice(null); 1200 } 1201 } 1202 } 1203 } 1204 } 1205 1206 private void broadcastActiveDevice(BluetoothDevice device) { 1207 logD("broadcastActiveDevice: " + device); 1208 Intent intent = new Intent(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED); 1209 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 1210 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT 1211 | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); 1212 sendBroadcastAsUser(intent, UserHandle.ALL, HeadsetService.BLUETOOTH_PERM); 1213 } 1214 1215 /** 1216 * Check whether it is OK to accept a headset connection from a remote device 1217 * 1218 * @param device remote device that initiates the connection 1219 * @return true if the connection is acceptable 1220 */ 1221 public boolean okToAcceptConnection(BluetoothDevice device) { 1222 // Check if this is an incoming connection in Quiet mode. 1223 if (mAdapterService.isQuietModeEnabled()) { 1224 Log.w(TAG, "okToAcceptConnection: return false as quiet mode enabled"); 1225 return false; 1226 } 1227 // Check priority and accept or reject the connection. 1228 // Note: Logic can be simplified, but keeping it this way for readability 1229 int priority = getPriority(device); 1230 int bondState = mAdapterService.getBondState(device); 1231 // If priority is undefined, it is likely that service discovery has not completed and peer 1232 // initiated the connection. Allow this connection only if the device is bonded or bonding 1233 boolean serviceDiscoveryPending = (priority == BluetoothProfile.PRIORITY_UNDEFINED) 1234 && (bondState == BluetoothDevice.BOND_BONDING 1235 || bondState == BluetoothDevice.BOND_BONDED); 1236 // Also allow connection when device is bonded/bonding and priority is ON/AUTO_CONNECT. 1237 boolean isEnabled = (priority == BluetoothProfile.PRIORITY_ON 1238 || priority == BluetoothProfile.PRIORITY_AUTO_CONNECT) 1239 && (bondState == BluetoothDevice.BOND_BONDED 1240 || bondState == BluetoothDevice.BOND_BONDING); 1241 if (!serviceDiscoveryPending && !isEnabled) { 1242 // Otherwise, reject the connection if no service discovery is pending and priority is 1243 // neither PRIORITY_ON nor PRIORITY_AUTO_CONNECT 1244 Log.w(TAG, "okToConnect: return false, priority=" + priority + ", bondState=" 1245 + bondState); 1246 return false; 1247 } 1248 List<BluetoothDevice> connectingConnectedDevices = 1249 getDevicesMatchingConnectionStates(CONNECTING_CONNECTED_STATES); 1250 if (connectingConnectedDevices.size() >= mMaxHeadsetConnections) { 1251 Log.w(TAG, "Maximum number of connections " + mMaxHeadsetConnections 1252 + " was reached, rejecting connection from " + device); 1253 return false; 1254 } 1255 return true; 1256 } 1257 1258 /** 1259 * Remove state machine in {@link #mStateMachines} for a {@link BluetoothDevice} 1260 * 1261 * @param device device whose state machine is to be removed. 1262 */ 1263 void removeStateMachine(BluetoothDevice device) { 1264 synchronized (mStateMachines) { 1265 HeadsetStateMachine stateMachine = mStateMachines.get(device); 1266 if (stateMachine == null) { 1267 Log.w(TAG, "removeStateMachine(), " + device + " does not have a state machine"); 1268 return; 1269 } 1270 Log.i(TAG, "removeStateMachine(), removing state machine for device: " + device); 1271 HeadsetObjectsFactory.getInstance().destroyStateMachine(stateMachine); 1272 mStateMachines.remove(device); 1273 } 1274 } 1275 1276 @Override 1277 public void dump(StringBuilder sb) { 1278 synchronized (mStateMachines) { 1279 super.dump(sb); 1280 ProfileService.println(sb, "mMaxHeadsetConnections: " + mMaxHeadsetConnections); 1281 ProfileService.println(sb, "DefaultMaxHeadsetConnections: " 1282 + mAdapterService.getMaxConnectedAudioDevices()); 1283 ProfileService.println(sb, "mActiveDevice: " + mActiveDevice); 1284 ProfileService.println(sb, "isInbandRingingEnabled: " + isInbandRingingEnabled()); 1285 ProfileService.println(sb, 1286 "isInbandRingingSupported: " + BluetoothHeadset.isInbandRingingSupported(this)); 1287 ProfileService.println(sb, 1288 "mInbandRingingRuntimeDisable: " + mInbandRingingRuntimeDisable); 1289 ProfileService.println(sb, "mAudioRouteAllowed: " + mAudioRouteAllowed); 1290 ProfileService.println(sb, "mForceScoAudio: " + mForceScoAudio); 1291 ProfileService.println(sb, "mCreated: " + mCreated); 1292 ProfileService.println(sb, "mStarted: " + mStarted); 1293 ProfileService.println(sb, 1294 "AudioManager.isBluetoothScoOn(): " + mSystemInterface.getAudioManager() 1295 .isBluetoothScoOn()); 1296 ProfileService.println(sb, "Telecom.isInCall(): " + mSystemInterface.isInCall()); 1297 ProfileService.println(sb, "Telecom.isRinging(): " + mSystemInterface.isRinging()); 1298 for (HeadsetStateMachine stateMachine : mStateMachines.values()) { 1299 ProfileService.println(sb, 1300 "==== StateMachine for " + stateMachine.getDevice() + " ===="); 1301 stateMachine.dump(sb); 1302 } 1303 } 1304 } 1305 1306 private static void logD(String message) { 1307 if (DBG) { 1308 Log.d(TAG, message); 1309 } 1310 } 1311} 1312