1 /* 2 * Copyright (c) 2014 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.hfpclient; 18 19 import android.annotation.RequiresPermission; 20 import android.bluetooth.BluetoothDevice; 21 import android.bluetooth.BluetoothHeadsetClient; 22 import android.bluetooth.BluetoothHeadsetClientCall; 23 import android.bluetooth.BluetoothProfile; 24 import android.bluetooth.BluetoothSinkAudioPolicy; 25 import android.bluetooth.BluetoothStatusCodes; 26 import android.bluetooth.IBluetoothHeadsetClient; 27 import android.content.AttributionSource; 28 import android.content.BroadcastReceiver; 29 import android.content.Context; 30 import android.content.Intent; 31 import android.content.IntentFilter; 32 import android.media.AudioManager; 33 import android.os.BatteryManager; 34 import android.os.Bundle; 35 import android.os.HandlerThread; 36 import android.os.Message; 37 import android.os.SystemProperties; 38 import android.sysprop.BluetoothProperties; 39 import android.util.Log; 40 41 import com.android.bluetooth.Utils; 42 import com.android.bluetooth.btservice.AdapterService; 43 import com.android.bluetooth.btservice.ProfileService; 44 import com.android.bluetooth.btservice.storage.DatabaseManager; 45 import com.android.internal.annotations.GuardedBy; 46 import com.android.internal.annotations.VisibleForTesting; 47 import com.android.modules.utils.SynchronousResultReceiver; 48 49 import java.util.ArrayList; 50 import java.util.HashMap; 51 import java.util.Iterator; 52 import java.util.List; 53 import java.util.Map; 54 import java.util.Objects; 55 import java.util.Set; 56 import java.util.UUID; 57 58 /** 59 * Provides Bluetooth Headset Client (HF Role) profile, as a service in the 60 * Bluetooth application. 61 * 62 * @hide 63 */ 64 public class HeadsetClientService extends ProfileService { 65 private static final boolean DBG = true; 66 private static final String TAG = "HeadsetClientService"; 67 68 // This is also used as a lock for shared data in {@link HeadsetClientService} 69 @GuardedBy("mStateMachineMap") 70 private final HashMap<BluetoothDevice, HeadsetClientStateMachine> mStateMachineMap = 71 new HashMap<>(); 72 73 private static HeadsetClientService sHeadsetClientService; 74 private NativeInterface mNativeInterface = null; 75 private HandlerThread mSmThread = null; 76 private HeadsetClientStateMachineFactory mSmFactory = null; 77 private DatabaseManager mDatabaseManager; 78 private AudioManager mAudioManager = null; 79 private BatteryManager mBatteryManager = null; 80 private int mLastBatteryLevel = -1; 81 // Maxinum number of devices we can try connecting to in one session 82 private static final int MAX_STATE_MACHINES_POSSIBLE = 100; 83 84 private final Object mStartStopLock = new Object(); 85 86 public static final String HFP_CLIENT_STOP_TAG = "hfp_client_stop_tag"; 87 isEnabled()88 public static boolean isEnabled() { 89 return BluetoothProperties.isProfileHfpHfEnabled().orElse(false); 90 } 91 92 @Override initBinder()93 public IProfileServiceBinder initBinder() { 94 return new BluetoothHeadsetClientBinder(this); 95 } 96 97 @Override start()98 protected boolean start() { 99 synchronized (mStartStopLock) { 100 if (DBG) { 101 Log.d(TAG, "start()"); 102 } 103 if (getHeadsetClientService() != null) { 104 Log.w(TAG, "start(): start called without stop"); 105 return false; 106 } 107 108 mDatabaseManager = Objects.requireNonNull( 109 AdapterService.getAdapterService().getDatabase(), 110 "DatabaseManager cannot be null when HeadsetClientService starts"); 111 112 // Setup the JNI service 113 mNativeInterface = NativeInterface.getInstance(); 114 mNativeInterface.initialize(); 115 116 mBatteryManager = getSystemService(BatteryManager.class); 117 118 mAudioManager = getSystemService(AudioManager.class); 119 if (mAudioManager == null) { 120 Log.e(TAG, "AudioManager service doesn't exist?"); 121 } else { 122 // start AudioManager in a known state 123 mAudioManager.setHfpEnabled(false); 124 } 125 126 mSmFactory = new HeadsetClientStateMachineFactory(); 127 synchronized (mStateMachineMap) { 128 mStateMachineMap.clear(); 129 } 130 131 IntentFilter filter = new IntentFilter(AudioManager.ACTION_VOLUME_CHANGED); 132 filter.addAction(Intent.ACTION_BATTERY_CHANGED); 133 registerReceiver(mBroadcastReceiver, filter); 134 135 // Start the HfpClientConnectionService to create connection with telecom when HFP 136 // connection is available. 137 Intent startIntent = new Intent(this, HfpClientConnectionService.class); 138 startService(startIntent); 139 140 // Create the thread on which all State Machines will run 141 mSmThread = new HandlerThread("HeadsetClient.SM"); 142 mSmThread.start(); 143 144 setHeadsetClientService(this); 145 AdapterService.getAdapterService().notifyActivityAttributionInfo( 146 getAttributionSource(), 147 AdapterService.ACTIVITY_ATTRIBUTION_NO_ACTIVE_DEVICE_ADDRESS); 148 return true; 149 } 150 } 151 152 @Override stop()153 protected boolean stop() { 154 synchronized (mStartStopLock) { 155 synchronized (HeadsetClientService.class) { 156 if (sHeadsetClientService == null) { 157 Log.w(TAG, "stop() called without start()"); 158 return false; 159 } 160 161 // Stop the HfpClientConnectionService. 162 AdapterService.getAdapterService().notifyActivityAttributionInfo( 163 getAttributionSource(), 164 AdapterService.ACTIVITY_ATTRIBUTION_NO_ACTIVE_DEVICE_ADDRESS); 165 Intent stopIntent = new Intent(this, HfpClientConnectionService.class); 166 sHeadsetClientService.stopService(stopIntent); 167 } 168 169 setHeadsetClientService(null); 170 171 unregisterReceiver(mBroadcastReceiver); 172 173 synchronized (mStateMachineMap) { 174 for (Iterator<Map.Entry<BluetoothDevice, HeadsetClientStateMachine>> it = 175 mStateMachineMap.entrySet().iterator(); it.hasNext(); ) { 176 HeadsetClientStateMachine sm = 177 mStateMachineMap.get((BluetoothDevice) it.next().getKey()); 178 sm.doQuit(); 179 it.remove(); 180 } 181 } 182 183 // Stop the handler thread 184 mSmThread.quit(); 185 mSmThread = null; 186 187 mNativeInterface.cleanup(); 188 mNativeInterface = null; 189 190 return true; 191 } 192 } 193 194 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 195 @Override 196 public void onReceive(Context context, Intent intent) { 197 String action = intent.getAction(); 198 199 // We handle the volume changes for Voice calls here since HFP audio volume control does 200 // not go through audio manager (audio mixer). see 201 // ({@link HeadsetClientStateMachine#SET_SPEAKER_VOLUME} in 202 // {@link HeadsetClientStateMachine} for details. 203 if (action.equals(AudioManager.ACTION_VOLUME_CHANGED)) { 204 if (DBG) { 205 Log.d(TAG, "Volume changed for stream: " + intent.getIntExtra( 206 AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1)); 207 } 208 int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1); 209 if (streamType == AudioManager.STREAM_VOICE_CALL) { 210 int streamValue = 211 intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, -1); 212 int hfVol = HeadsetClientStateMachine.amToHfVol(streamValue); 213 if (DBG) { 214 Log.d(TAG, 215 "Setting volume to audio manager: " + streamValue + " hands free: " 216 + hfVol); 217 } 218 mAudioManager.setHfpVolume(hfVol); 219 synchronized (mStateMachineMap) { 220 for (HeadsetClientStateMachine sm : mStateMachineMap.values()) { 221 if (sm != null) { 222 sm.sendMessage(HeadsetClientStateMachine.SET_SPEAKER_VOLUME, 223 streamValue); 224 } 225 } 226 } 227 } 228 } else if (action.equals(Intent.ACTION_BATTERY_CHANGED)) { 229 int batteryIndicatorID = 2; 230 int batteryLevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0); 231 232 if (batteryLevel == mLastBatteryLevel) { 233 return; 234 } 235 mLastBatteryLevel = batteryLevel; 236 237 if (DBG) { 238 Log.d(TAG, 239 "Send battery level update BIEV(2," + batteryLevel + ") command"); 240 } 241 242 synchronized (mStateMachineMap) { 243 for (HeadsetClientStateMachine sm : mStateMachineMap.values()) { 244 if (sm != null) { 245 sm.sendMessage(HeadsetClientStateMachine.SEND_BIEV, 246 batteryIndicatorID, 247 batteryLevel); 248 } 249 } 250 } 251 } 252 } 253 }; 254 toLegacyCall(HfpClientCall call)255 private static BluetoothHeadsetClientCall toLegacyCall(HfpClientCall call) { 256 if (call == null) return null; 257 return new BluetoothHeadsetClientCall(call.getDevice(), call.getId(), call.getUUID(), 258 call.getState(), call.getNumber(), call.isMultiParty(), call.isOutgoing(), 259 call.isInBandRing()); 260 } 261 262 /** 263 * Handlers for incoming service calls 264 */ 265 @VisibleForTesting 266 static class BluetoothHeadsetClientBinder extends IBluetoothHeadsetClient.Stub 267 implements IProfileServiceBinder { 268 private HeadsetClientService mService; 269 BluetoothHeadsetClientBinder(HeadsetClientService svc)270 BluetoothHeadsetClientBinder(HeadsetClientService svc) { 271 mService = svc; 272 } 273 274 @Override cleanup()275 public void cleanup() { 276 mService = null; 277 } 278 279 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) getService(AttributionSource source)280 private HeadsetClientService getService(AttributionSource source) { 281 if (Utils.isInstrumentationTestMode()) { 282 return mService; 283 } 284 if (!Utils.checkServiceAvailable(mService, TAG) 285 || !Utils.checkCallerIsSystemOrActiveOrManagedUser(mService, TAG) 286 || !Utils.checkConnectPermissionForDataDelivery(mService, source, TAG)) { 287 return null; 288 } 289 return mService; 290 } 291 292 @Override connect(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver)293 public void connect(BluetoothDevice device, AttributionSource source, 294 SynchronousResultReceiver receiver) { 295 try { 296 HeadsetClientService service = getService(source); 297 boolean defaultValue = false; 298 if (service != null) { 299 defaultValue = service.connect(device); 300 } 301 receiver.send(defaultValue); 302 } catch (RuntimeException e) { 303 receiver.propagateException(e); 304 } 305 } 306 307 @Override disconnect(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver)308 public void disconnect(BluetoothDevice device, AttributionSource source, 309 SynchronousResultReceiver receiver) { 310 try { 311 HeadsetClientService service = getService(source); 312 boolean defaultValue = false; 313 if (service != null) { 314 defaultValue = service.disconnect(device); 315 } 316 receiver.send(defaultValue); 317 } catch (RuntimeException e) { 318 receiver.propagateException(e); 319 } 320 } 321 322 @Override getConnectedDevices(AttributionSource source, SynchronousResultReceiver receiver)323 public void getConnectedDevices(AttributionSource source, 324 SynchronousResultReceiver receiver) { 325 try { 326 HeadsetClientService service = getService(source); 327 List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>(0); 328 if (service != null) { 329 defaultValue = service.getConnectedDevices(); 330 } 331 receiver.send(defaultValue); 332 } catch (RuntimeException e) { 333 receiver.propagateException(e); 334 } 335 } 336 337 @Override getDevicesMatchingConnectionStates(int[] states, AttributionSource source, SynchronousResultReceiver receiver)338 public void getDevicesMatchingConnectionStates(int[] states, 339 AttributionSource source, SynchronousResultReceiver receiver) { 340 try { 341 HeadsetClientService service = getService(source); 342 List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>(0); 343 if (service != null) { 344 defaultValue = service.getDevicesMatchingConnectionStates(states); 345 } 346 receiver.send(defaultValue); 347 } catch (RuntimeException e) { 348 receiver.propagateException(e); 349 } 350 } 351 352 @Override getConnectionState(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver)353 public void getConnectionState(BluetoothDevice device, AttributionSource source, 354 SynchronousResultReceiver receiver) { 355 try { 356 HeadsetClientService service = getService(source); 357 int defaultValue = BluetoothProfile.STATE_DISCONNECTED; 358 if (service != null) { 359 defaultValue = service.getConnectionState(device); 360 } 361 receiver.send(defaultValue); 362 } catch (RuntimeException e) { 363 receiver.propagateException(e); 364 } 365 } 366 367 @Override setConnectionPolicy(BluetoothDevice device, int connectionPolicy, AttributionSource source, SynchronousResultReceiver receiver)368 public void setConnectionPolicy(BluetoothDevice device, int connectionPolicy, 369 AttributionSource source, SynchronousResultReceiver receiver) { 370 try { 371 HeadsetClientService service = getService(source); 372 boolean defaultValue = false; 373 if (service != null) { 374 defaultValue = service.setConnectionPolicy(device, connectionPolicy); 375 } 376 receiver.send(defaultValue); 377 } catch (RuntimeException e) { 378 receiver.propagateException(e); 379 } 380 } 381 382 @Override getConnectionPolicy(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver)383 public void getConnectionPolicy(BluetoothDevice device, AttributionSource source, 384 SynchronousResultReceiver receiver) { 385 try { 386 HeadsetClientService service = getService(source); 387 int defaultValue = BluetoothProfile.CONNECTION_POLICY_UNKNOWN; 388 if (service != null) { 389 defaultValue = service.getConnectionPolicy(device); 390 } 391 receiver.send(defaultValue); 392 } catch (RuntimeException e) { 393 receiver.propagateException(e); 394 } 395 } 396 397 @Override startVoiceRecognition(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver)398 public void startVoiceRecognition(BluetoothDevice device, AttributionSource source, 399 SynchronousResultReceiver receiver) { 400 try { 401 HeadsetClientService service = getService(source); 402 boolean defaultValue = false; 403 if (service != null) { 404 defaultValue = service.startVoiceRecognition(device); 405 } 406 receiver.send(defaultValue); 407 } catch (RuntimeException e) { 408 receiver.propagateException(e); 409 } 410 } 411 412 @Override stopVoiceRecognition(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver)413 public void stopVoiceRecognition(BluetoothDevice device, AttributionSource source, 414 SynchronousResultReceiver receiver) { 415 try { 416 HeadsetClientService service = getService(source); 417 boolean defaultValue = false; 418 if (service != null) { 419 defaultValue = service.stopVoiceRecognition(device); 420 } 421 receiver.send(defaultValue); 422 } catch (RuntimeException e) { 423 receiver.propagateException(e); 424 } 425 } 426 427 @Override getAudioState(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver)428 public void getAudioState(BluetoothDevice device, AttributionSource source, 429 SynchronousResultReceiver receiver) { 430 try { 431 HeadsetClientService service = getService(source); 432 int defaultValue = BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED; 433 if (service != null) { 434 defaultValue = service.getAudioState(device); 435 } 436 receiver.send(defaultValue); 437 } catch (RuntimeException e) { 438 receiver.propagateException(e); 439 } 440 } 441 442 @Override setAudioRouteAllowed(BluetoothDevice device, boolean allowed, AttributionSource source, SynchronousResultReceiver receiver)443 public void setAudioRouteAllowed(BluetoothDevice device, boolean allowed, 444 AttributionSource source, SynchronousResultReceiver receiver) { 445 try { 446 HeadsetClientService service = getService(source); 447 if (service != null) { 448 service.setAudioRouteAllowed(device, allowed); 449 } 450 receiver.send(null); 451 } catch (RuntimeException e) { 452 receiver.propagateException(e); 453 } 454 } 455 456 @Override getAudioRouteAllowed(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver)457 public void getAudioRouteAllowed(BluetoothDevice device, AttributionSource source, 458 SynchronousResultReceiver receiver) { 459 try { 460 HeadsetClientService service = getService(source); 461 boolean defaultValue = false; 462 if (service != null) { 463 defaultValue = service.getAudioRouteAllowed(device); 464 } 465 receiver.send(defaultValue); 466 } catch (RuntimeException e) { 467 receiver.propagateException(e); 468 } 469 } 470 471 @Override connectAudio(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver)472 public void connectAudio(BluetoothDevice device, AttributionSource source, 473 SynchronousResultReceiver receiver) { 474 try { 475 HeadsetClientService service = getService(source); 476 boolean defaultValue = false; 477 if (service != null) { 478 defaultValue = service.connectAudio(device); 479 } 480 receiver.send(defaultValue); 481 } catch (RuntimeException e) { 482 receiver.propagateException(e); 483 } 484 } 485 486 @Override disconnectAudio(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver)487 public void disconnectAudio(BluetoothDevice device, AttributionSource source, 488 SynchronousResultReceiver receiver) { 489 try { 490 HeadsetClientService service = getService(source); 491 boolean defaultValue = false; 492 if (service != null) { 493 defaultValue = service.disconnectAudio(device); 494 } 495 receiver.send(defaultValue); 496 } catch (RuntimeException e) { 497 receiver.propagateException(e); 498 } 499 } 500 501 @Override acceptCall(BluetoothDevice device, int flag, AttributionSource source, SynchronousResultReceiver receiver)502 public void acceptCall(BluetoothDevice device, int flag, AttributionSource source, 503 SynchronousResultReceiver receiver) { 504 try { 505 HeadsetClientService service = getService(source); 506 boolean defaultValue = false; 507 if (service != null) { 508 defaultValue = service.acceptCall(device, flag); 509 } 510 receiver.send(defaultValue); 511 } catch (RuntimeException e) { 512 receiver.propagateException(e); 513 } 514 } 515 516 @Override rejectCall(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver)517 public void rejectCall(BluetoothDevice device, AttributionSource source, 518 SynchronousResultReceiver receiver) { 519 try { 520 HeadsetClientService service = getService(source); 521 boolean defaultValue = false; 522 if (service != null) { 523 defaultValue = service.rejectCall(device); 524 } 525 receiver.send(defaultValue); 526 } catch (RuntimeException e) { 527 receiver.propagateException(e); 528 } 529 } 530 531 @Override holdCall(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver)532 public void holdCall(BluetoothDevice device, AttributionSource source, 533 SynchronousResultReceiver receiver) { 534 try { 535 HeadsetClientService service = getService(source); 536 boolean defaultValue = false; 537 if (service != null) { 538 defaultValue = service.holdCall(device); 539 } 540 receiver.send(defaultValue); 541 } catch (RuntimeException e) { 542 receiver.propagateException(e); 543 } 544 } 545 546 @Override terminateCall(BluetoothDevice device, BluetoothHeadsetClientCall call, AttributionSource source, SynchronousResultReceiver receiver)547 public void terminateCall(BluetoothDevice device, BluetoothHeadsetClientCall call, 548 AttributionSource source, SynchronousResultReceiver receiver) { 549 try { 550 HeadsetClientService service = getService(source); 551 boolean defaultValue = false; 552 if (service != null) { 553 defaultValue = service.terminateCall(device, 554 call != null ? call.getUUID() : null); 555 } else { 556 Log.w(TAG, "service is null"); 557 } 558 receiver.send(defaultValue); 559 } catch (RuntimeException e) { 560 receiver.propagateException(e); 561 } 562 } 563 564 @Override explicitCallTransfer(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver)565 public void explicitCallTransfer(BluetoothDevice device, AttributionSource source, 566 SynchronousResultReceiver receiver) { 567 try { 568 HeadsetClientService service = getService(source); 569 boolean defaultValue = false; 570 if (service != null) { 571 defaultValue = service.explicitCallTransfer(device); 572 } 573 receiver.send(defaultValue); 574 } catch (RuntimeException e) { 575 receiver.propagateException(e); 576 } 577 } 578 579 @Override enterPrivateMode(BluetoothDevice device, int index, AttributionSource source, SynchronousResultReceiver receiver)580 public void enterPrivateMode(BluetoothDevice device, int index, 581 AttributionSource source, SynchronousResultReceiver receiver) { 582 try { 583 HeadsetClientService service = getService(source); 584 boolean defaultValue = false; 585 if (service != null) { 586 defaultValue = service.enterPrivateMode(device, index); 587 } 588 receiver.send(defaultValue); 589 } catch (RuntimeException e) { 590 receiver.propagateException(e); 591 } 592 } 593 594 @Override dial(BluetoothDevice device, String number, AttributionSource source, SynchronousResultReceiver receiver)595 public void dial(BluetoothDevice device, String number, 596 AttributionSource source, SynchronousResultReceiver receiver) { 597 try { 598 HeadsetClientService service = getService(source); 599 BluetoothHeadsetClientCall defaultValue = null; 600 if (service != null) { 601 HfpClientCall call = service.dial(device, number); 602 defaultValue = toLegacyCall(call); 603 } 604 receiver.send(defaultValue); 605 } catch (RuntimeException e) { 606 receiver.propagateException(e); 607 } 608 } 609 610 @Override getCurrentCalls(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver)611 public void getCurrentCalls(BluetoothDevice device, 612 AttributionSource source, SynchronousResultReceiver receiver) { 613 try { 614 HeadsetClientService service = getService(source); 615 List<BluetoothHeadsetClientCall> defaultValue = new ArrayList<>(); 616 if (service != null) { 617 List<HfpClientCall> calls = service.getCurrentCalls(device); 618 if (calls != null) { 619 for (HfpClientCall call : calls) { 620 defaultValue.add(toLegacyCall(call)); 621 } 622 } 623 } 624 receiver.send(defaultValue); 625 } catch (RuntimeException e) { 626 receiver.propagateException(e); 627 } 628 } 629 630 @Override sendDTMF(BluetoothDevice device, byte code, AttributionSource source, SynchronousResultReceiver receiver)631 public void sendDTMF(BluetoothDevice device, byte code, AttributionSource source, 632 SynchronousResultReceiver receiver) { 633 try { 634 HeadsetClientService service = getService(source); 635 boolean defaultValue = false; 636 if (service != null) { 637 defaultValue = service.sendDTMF(device, code); 638 } 639 receiver.send(defaultValue); 640 } catch (RuntimeException e) { 641 receiver.propagateException(e); 642 } 643 } 644 645 @Override getLastVoiceTagNumber(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver)646 public void getLastVoiceTagNumber(BluetoothDevice device, AttributionSource source, 647 SynchronousResultReceiver receiver) { 648 try { 649 HeadsetClientService service = getService(source); 650 boolean defaultValue = false; 651 if (service != null) { 652 defaultValue = service.getLastVoiceTagNumber(device); 653 } 654 receiver.send(defaultValue); 655 } catch (RuntimeException e) { 656 receiver.propagateException(e); 657 } 658 } 659 660 @Override getCurrentAgEvents(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver)661 public void getCurrentAgEvents(BluetoothDevice device, AttributionSource source, 662 SynchronousResultReceiver receiver) { 663 try { 664 HeadsetClientService service = getService(source); 665 Bundle defaultValue = null; 666 if (service != null) { 667 defaultValue = service.getCurrentAgEvents(device); 668 } 669 receiver.send(defaultValue); 670 } catch (RuntimeException e) { 671 receiver.propagateException(e); 672 } 673 } 674 675 @Override sendVendorAtCommand(BluetoothDevice device, int vendorId, String atCommand, AttributionSource source, SynchronousResultReceiver receiver)676 public void sendVendorAtCommand(BluetoothDevice device, int vendorId, String atCommand, 677 AttributionSource source, SynchronousResultReceiver receiver) { 678 try { 679 HeadsetClientService service = getService(source); 680 boolean defaultValue = false; 681 if (service != null) { 682 defaultValue = service.sendVendorAtCommand(device, vendorId, atCommand); 683 } 684 receiver.send(defaultValue); 685 } catch (RuntimeException e) { 686 receiver.propagateException(e); 687 } 688 } 689 690 @Override getCurrentAgFeatures(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver)691 public void getCurrentAgFeatures(BluetoothDevice device, AttributionSource source, 692 SynchronousResultReceiver receiver) { 693 try { 694 HeadsetClientService service = getService(source); 695 Bundle defaultValue = null; 696 if (service != null) { 697 defaultValue = service.getCurrentAgFeaturesBundle(device); 698 } 699 receiver.send(defaultValue); 700 } catch (RuntimeException e) { 701 receiver.propagateException(e); 702 } 703 } 704 } 705 706 // API methods getHeadsetClientService()707 public static synchronized HeadsetClientService getHeadsetClientService() { 708 if (sHeadsetClientService == null) { 709 Log.w(TAG, "getHeadsetClientService(): service is null"); 710 return null; 711 } 712 if (!sHeadsetClientService.isAvailable()) { 713 Log.w(TAG, "getHeadsetClientService(): service is not available "); 714 return null; 715 } 716 return sHeadsetClientService; 717 } 718 719 /** Set a {@link HeadsetClientService} instance. */ 720 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) setHeadsetClientService(HeadsetClientService instance)721 public static synchronized void setHeadsetClientService(HeadsetClientService instance) { 722 if (DBG) { 723 Log.d(TAG, "setHeadsetClientService(): set to: " + instance); 724 } 725 sHeadsetClientService = instance; 726 } 727 connect(BluetoothDevice device)728 public boolean connect(BluetoothDevice device) { 729 if (DBG) { 730 Log.d(TAG, "connect " + device); 731 } 732 if (getConnectionPolicy(device) == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) { 733 Log.w(TAG, "Connection not allowed: <" + device.getAddress() 734 + "> is CONNECTION_POLICY_FORBIDDEN"); 735 return false; 736 } 737 HeadsetClientStateMachine sm = getStateMachine(device, true); 738 if (sm == null) { 739 Log.e(TAG, "Cannot allocate SM for device " + device); 740 return false; 741 } 742 743 sm.sendMessage(HeadsetClientStateMachine.CONNECT, device); 744 return true; 745 } 746 747 /** 748 * Disconnects hfp client for the remote bluetooth device 749 * 750 * @param device is the device with which we are attempting to disconnect the profile 751 * @return true if hfp client profile successfully disconnected, false otherwise 752 */ disconnect(BluetoothDevice device)753 public boolean disconnect(BluetoothDevice device) { 754 HeadsetClientStateMachine sm = getStateMachine(device); 755 if (sm == null) { 756 Log.e(TAG, "SM does not exist for device " + device); 757 return false; 758 } 759 760 int connectionState = sm.getConnectionState(device); 761 if (connectionState != BluetoothProfile.STATE_CONNECTED 762 && connectionState != BluetoothProfile.STATE_CONNECTING) { 763 return false; 764 } 765 766 sm.sendMessage(HeadsetClientStateMachine.DISCONNECT, device); 767 return true; 768 } 769 770 /** 771 * @return A list of connected {@link BluetoothDevice}. 772 */ getConnectedDevices()773 public List<BluetoothDevice> getConnectedDevices() { 774 ArrayList<BluetoothDevice> connectedDevices = new ArrayList<>(); 775 synchronized (mStateMachineMap) { 776 for (BluetoothDevice bd : mStateMachineMap.keySet()) { 777 HeadsetClientStateMachine sm = mStateMachineMap.get(bd); 778 if (sm != null && sm.getConnectionState(bd) == BluetoothProfile.STATE_CONNECTED) { 779 connectedDevices.add(bd); 780 } 781 } 782 } 783 return connectedDevices; 784 } 785 getDevicesMatchingConnectionStates(int[] states)786 List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 787 List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>(); 788 synchronized (mStateMachineMap) { 789 for (BluetoothDevice bd : mStateMachineMap.keySet()) { 790 for (int state : states) { 791 HeadsetClientStateMachine sm = mStateMachineMap.get(bd); 792 if (sm != null && sm.getConnectionState(bd) == state) { 793 devices.add(bd); 794 } 795 } 796 } 797 } 798 return devices; 799 } 800 801 /** 802 * Get the current connection state of the profile 803 * 804 * @param device is the remote bluetooth device 805 * @return {@link BluetoothProfile#STATE_DISCONNECTED} if this profile is disconnected, 806 * {@link BluetoothProfile#STATE_CONNECTING} if this profile is being connected, 807 * {@link BluetoothProfile#STATE_CONNECTED} if this profile is connected, or 808 * {@link BluetoothProfile#STATE_DISCONNECTING} if this profile is being disconnected 809 */ getConnectionState(BluetoothDevice device)810 public int getConnectionState(BluetoothDevice device) { 811 HeadsetClientStateMachine sm = getStateMachine(device); 812 if (sm != null) { 813 return sm.getConnectionState(device); 814 } 815 816 return BluetoothProfile.STATE_DISCONNECTED; 817 } 818 819 /** 820 * Set connection policy of the profile and connects it if connectionPolicy is 821 * {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED} or disconnects if connectionPolicy is 822 * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN} 823 * 824 * <p> The device should already be paired. 825 * Connection policy can be one of: 826 * {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED}, 827 * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN}, 828 * {@link BluetoothProfile#CONNECTION_POLICY_UNKNOWN} 829 * 830 * @param device Paired bluetooth device 831 * @param connectionPolicy is the connection policy to set to for this profile 832 * @return true if connectionPolicy is set, false on error 833 */ setConnectionPolicy(BluetoothDevice device, int connectionPolicy)834 public boolean setConnectionPolicy(BluetoothDevice device, int connectionPolicy) { 835 if (DBG) { 836 Log.d(TAG, "Saved connectionPolicy " + device + " = " + connectionPolicy); 837 } 838 839 if (!mDatabaseManager.setProfileConnectionPolicy(device, BluetoothProfile.HEADSET_CLIENT, 840 connectionPolicy)) { 841 return false; 842 } 843 if (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED) { 844 connect(device); 845 } else if (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) { 846 disconnect(device); 847 } 848 return true; 849 } 850 851 /** 852 * Get the connection policy of the profile. 853 * 854 * <p> The connection policy can be any of: 855 * {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED}, 856 * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN}, 857 * {@link BluetoothProfile#CONNECTION_POLICY_UNKNOWN} 858 * 859 * @param device Bluetooth device 860 * @return connection policy of the device 861 * @hide 862 */ getConnectionPolicy(BluetoothDevice device)863 public int getConnectionPolicy(BluetoothDevice device) { 864 return mDatabaseManager 865 .getProfileConnectionPolicy(device, BluetoothProfile.HEADSET_CLIENT); 866 } 867 startVoiceRecognition(BluetoothDevice device)868 boolean startVoiceRecognition(BluetoothDevice device) { 869 HeadsetClientStateMachine sm = getStateMachine(device); 870 if (sm == null) { 871 Log.e(TAG, "SM does not exist for device " + device); 872 return false; 873 } 874 int connectionState = sm.getConnectionState(device); 875 if (connectionState != BluetoothProfile.STATE_CONNECTED) { 876 return false; 877 } 878 sm.sendMessage(HeadsetClientStateMachine.VOICE_RECOGNITION_START); 879 return true; 880 } 881 stopVoiceRecognition(BluetoothDevice device)882 boolean stopVoiceRecognition(BluetoothDevice device) { 883 HeadsetClientStateMachine sm = getStateMachine(device); 884 if (sm == null) { 885 Log.e(TAG, "SM does not exist for device " + device); 886 return false; 887 } 888 int connectionState = sm.getConnectionState(device); 889 if (connectionState != BluetoothProfile.STATE_CONNECTED) { 890 return false; 891 } 892 sm.sendMessage(HeadsetClientStateMachine.VOICE_RECOGNITION_STOP); 893 return true; 894 } 895 896 /** 897 * Gets audio state of the connection with {@code device}. 898 * 899 * <p>Can be one of {@link STATE_AUDIO_CONNECTED}, {@link STATE_AUDIO_CONNECTING}, or 900 * {@link STATE_AUDIO_DISCONNECTED}. 901 */ getAudioState(BluetoothDevice device)902 public int getAudioState(BluetoothDevice device) { 903 HeadsetClientStateMachine sm = getStateMachine(device); 904 if (sm == null) { 905 Log.e(TAG, "SM does not exist for device " + device); 906 return -1; 907 } 908 909 return sm.getAudioState(device); 910 } 911 setAudioRouteAllowed(BluetoothDevice device, boolean allowed)912 public void setAudioRouteAllowed(BluetoothDevice device, boolean allowed) { 913 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 914 Log.i(TAG, "setAudioRouteAllowed: device=" + device + ", allowed=" + allowed + ", " 915 + Utils.getUidPidString()); 916 HeadsetClientStateMachine sm = mStateMachineMap.get(device); 917 if (sm != null) { 918 sm.setAudioRouteAllowed(allowed); 919 } 920 } 921 getAudioRouteAllowed(BluetoothDevice device)922 public boolean getAudioRouteAllowed(BluetoothDevice device) { 923 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 924 HeadsetClientStateMachine sm = mStateMachineMap.get(device); 925 if (sm != null) { 926 return sm.getAudioRouteAllowed(); 927 } 928 return false; 929 } 930 931 /** 932 * sends the {@link BluetoothSinkAudioPolicy} object to the state machine of the corresponding 933 * device to store and send to the remote device using Android specific AT commands. 934 * 935 * @param device for whom the policies to be set 936 * @param policies to be set policies 937 */ setAudioPolicy(BluetoothDevice device, BluetoothSinkAudioPolicy policies)938 public void setAudioPolicy(BluetoothDevice device, BluetoothSinkAudioPolicy policies) { 939 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 940 Log.i(TAG, "setAudioPolicy: device=" + device + ", " + policies.toString() + ", " 941 + Utils.getUidPidString()); 942 HeadsetClientStateMachine sm = getStateMachine(device); 943 if (sm != null) { 944 sm.setAudioPolicy(policies); 945 } 946 } 947 948 /** 949 * sets the audio policy feature support status for the corresponding device. 950 * 951 * @param device for whom the policies to be set 952 * @param supported support status 953 */ setAudioPolicyRemoteSupported(BluetoothDevice device, boolean supported)954 public void setAudioPolicyRemoteSupported(BluetoothDevice device, boolean supported) { 955 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 956 Log.i(TAG, "setAudioPolicyRemoteSupported: " + supported); 957 HeadsetClientStateMachine sm = getStateMachine(device); 958 if (sm != null) { 959 sm.setAudioPolicyRemoteSupported(supported); 960 } 961 } 962 963 /** 964 * gets the audio policy feature support status for the corresponding device. 965 * 966 * @param device for whom the policies to be set 967 * @return int support status 968 */ getAudioPolicyRemoteSupported(BluetoothDevice device)969 public int getAudioPolicyRemoteSupported(BluetoothDevice device) { 970 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 971 HeadsetClientStateMachine sm = getStateMachine(device); 972 if (sm != null) { 973 return sm.getAudioPolicyRemoteSupported(); 974 } 975 return BluetoothStatusCodes.FEATURE_NOT_CONFIGURED; 976 } 977 connectAudio(BluetoothDevice device)978 public boolean connectAudio(BluetoothDevice device) { 979 Log.i(TAG, "connectAudio: device=" + device + ", " + Utils.getUidPidString()); 980 HeadsetClientStateMachine sm = getStateMachine(device); 981 if (sm == null) { 982 Log.e(TAG, "SM does not exist for device " + device); 983 return false; 984 } 985 986 if (!sm.isConnected()) { 987 return false; 988 } 989 if (sm.isAudioOn()) { 990 return false; 991 } 992 sm.sendMessage(HeadsetClientStateMachine.CONNECT_AUDIO); 993 return true; 994 } 995 disconnectAudio(BluetoothDevice device)996 public boolean disconnectAudio(BluetoothDevice device) { 997 HeadsetClientStateMachine sm = getStateMachine(device); 998 if (sm == null) { 999 Log.e(TAG, "SM does not exist for device " + device); 1000 return false; 1001 } 1002 1003 if (!sm.isAudioOn()) { 1004 return false; 1005 } 1006 sm.sendMessage(HeadsetClientStateMachine.DISCONNECT_AUDIO); 1007 return true; 1008 } 1009 holdCall(BluetoothDevice device)1010 public boolean holdCall(BluetoothDevice device) { 1011 HeadsetClientStateMachine sm = getStateMachine(device); 1012 if (sm == null) { 1013 Log.e(TAG, "SM does not exist for device " + device); 1014 return false; 1015 } 1016 1017 int connectionState = sm.getConnectionState(device); 1018 if (connectionState != BluetoothProfile.STATE_CONNECTED 1019 && connectionState != BluetoothProfile.STATE_CONNECTING) { 1020 return false; 1021 } 1022 Message msg = sm.obtainMessage(HeadsetClientStateMachine.HOLD_CALL); 1023 sm.sendMessage(msg); 1024 return true; 1025 } 1026 acceptCall(BluetoothDevice device, int flag)1027 public boolean acceptCall(BluetoothDevice device, int flag) { 1028 /* Phonecalls from a single device are supported, hang up any calls on the other phone */ 1029 synchronized (mStateMachineMap) { 1030 for (Map.Entry<BluetoothDevice, HeadsetClientStateMachine> entry : mStateMachineMap 1031 .entrySet()) { 1032 if (entry.getValue() == null || entry.getKey().equals(device)) { 1033 continue; 1034 } 1035 int connectionState = entry.getValue().getConnectionState(entry.getKey()); 1036 if (DBG) { 1037 Log.d(TAG, 1038 "Accepting a call on device " + device + ". Possibly disconnecting on " 1039 + entry.getValue()); 1040 } 1041 if (connectionState == BluetoothProfile.STATE_CONNECTED) { 1042 entry.getValue() 1043 .obtainMessage(HeadsetClientStateMachine.TERMINATE_CALL) 1044 .sendToTarget(); 1045 } 1046 } 1047 } 1048 HeadsetClientStateMachine sm = getStateMachine(device); 1049 if (sm == null) { 1050 Log.e(TAG, "SM does not exist for device " + device); 1051 return false; 1052 } 1053 1054 int connectionState = sm.getConnectionState(device); 1055 if (connectionState != BluetoothProfile.STATE_CONNECTED) { 1056 return false; 1057 } 1058 Message msg = sm.obtainMessage(HeadsetClientStateMachine.ACCEPT_CALL); 1059 msg.arg1 = flag; 1060 sm.sendMessage(msg); 1061 return true; 1062 } 1063 rejectCall(BluetoothDevice device)1064 public boolean rejectCall(BluetoothDevice device) { 1065 HeadsetClientStateMachine sm = getStateMachine(device); 1066 if (sm == null) { 1067 Log.e(TAG, "SM does not exist for device " + device); 1068 return false; 1069 } 1070 1071 int connectionState = sm.getConnectionState(device); 1072 if (connectionState != BluetoothProfile.STATE_CONNECTED 1073 && connectionState != BluetoothProfile.STATE_CONNECTING) { 1074 return false; 1075 } 1076 1077 Message msg = sm.obtainMessage(HeadsetClientStateMachine.REJECT_CALL); 1078 sm.sendMessage(msg); 1079 return true; 1080 } 1081 terminateCall(BluetoothDevice device, UUID uuid)1082 public boolean terminateCall(BluetoothDevice device, UUID uuid) { 1083 HeadsetClientStateMachine sm = getStateMachine(device); 1084 if (sm == null) { 1085 Log.e(TAG, "SM does not exist for device " + device); 1086 return false; 1087 } 1088 1089 int connectionState = sm.getConnectionState(device); 1090 if (connectionState != BluetoothProfile.STATE_CONNECTED 1091 && connectionState != BluetoothProfile.STATE_CONNECTING) { 1092 return false; 1093 } 1094 1095 Message msg = sm.obtainMessage(HeadsetClientStateMachine.TERMINATE_CALL); 1096 msg.obj = uuid; 1097 sm.sendMessage(msg); 1098 return true; 1099 } 1100 enterPrivateMode(BluetoothDevice device, int index)1101 public boolean enterPrivateMode(BluetoothDevice device, int index) { 1102 HeadsetClientStateMachine sm = getStateMachine(device); 1103 if (sm == null) { 1104 Log.e(TAG, "SM does not exist for device " + device); 1105 return false; 1106 } 1107 1108 int connectionState = sm.getConnectionState(device); 1109 if (connectionState != BluetoothProfile.STATE_CONNECTED 1110 && connectionState != BluetoothProfile.STATE_CONNECTING) { 1111 return false; 1112 } 1113 1114 Message msg = sm.obtainMessage(HeadsetClientStateMachine.ENTER_PRIVATE_MODE); 1115 msg.arg1 = index; 1116 sm.sendMessage(msg); 1117 return true; 1118 } 1119 dial(BluetoothDevice device, String number)1120 public HfpClientCall dial(BluetoothDevice device, String number) { 1121 HeadsetClientStateMachine sm = getStateMachine(device); 1122 if (sm == null) { 1123 Log.e(TAG, "SM does not exist for device " + device); 1124 return null; 1125 } 1126 1127 int connectionState = sm.getConnectionState(device); 1128 if (connectionState != BluetoothProfile.STATE_CONNECTED 1129 && connectionState != BluetoothProfile.STATE_CONNECTING) { 1130 return null; 1131 } 1132 1133 // Some platform does not support three way calling (ex: watch) 1134 final boolean support_three_way_calling = SystemProperties 1135 .getBoolean("bluetooth.headset_client.three_way_calling.enabled", true); 1136 if (!support_three_way_calling && !getCurrentCalls(device).isEmpty()) { 1137 Log.e(TAG, String.format("dial(%s): Line is busy, reject dialing", device)); 1138 return null; 1139 } 1140 1141 HfpClientCall call = new HfpClientCall(device, 1142 HeadsetClientStateMachine.HF_ORIGINATED_CALL_ID, 1143 HfpClientCall.CALL_STATE_DIALING, number, false /* multiparty */, 1144 true /* outgoing */, sm.getInBandRing()); 1145 Message msg = sm.obtainMessage(HeadsetClientStateMachine.DIAL_NUMBER); 1146 msg.obj = call; 1147 sm.sendMessage(msg); 1148 return call; 1149 } 1150 sendDTMF(BluetoothDevice device, byte code)1151 public boolean sendDTMF(BluetoothDevice device, byte code) { 1152 HeadsetClientStateMachine sm = getStateMachine(device); 1153 if (sm == null) { 1154 Log.e(TAG, "SM does not exist for device " + device); 1155 return false; 1156 } 1157 1158 int connectionState = sm.getConnectionState(device); 1159 if (connectionState != BluetoothProfile.STATE_CONNECTED 1160 && connectionState != BluetoothProfile.STATE_CONNECTING) { 1161 return false; 1162 } 1163 Message msg = sm.obtainMessage(HeadsetClientStateMachine.SEND_DTMF); 1164 msg.arg1 = code; 1165 sm.sendMessage(msg); 1166 return true; 1167 } 1168 getLastVoiceTagNumber(BluetoothDevice device)1169 public boolean getLastVoiceTagNumber(BluetoothDevice device) { 1170 return false; 1171 } 1172 getCurrentCalls(BluetoothDevice device)1173 public List<HfpClientCall> getCurrentCalls(BluetoothDevice device) { 1174 HeadsetClientStateMachine sm = getStateMachine(device); 1175 if (sm == null) { 1176 Log.e(TAG, "SM does not exist for device " + device); 1177 return null; 1178 } 1179 1180 int connectionState = sm.getConnectionState(device); 1181 if (connectionState != BluetoothProfile.STATE_CONNECTED) { 1182 return null; 1183 } 1184 return sm.getCurrentCalls(); 1185 } 1186 explicitCallTransfer(BluetoothDevice device)1187 public boolean explicitCallTransfer(BluetoothDevice device) { 1188 HeadsetClientStateMachine sm = getStateMachine(device); 1189 if (sm == null) { 1190 Log.e(TAG, "SM does not exist for device " + device); 1191 return false; 1192 } 1193 1194 int connectionState = sm.getConnectionState(device); 1195 if (connectionState != BluetoothProfile.STATE_CONNECTED 1196 && connectionState != BluetoothProfile.STATE_CONNECTING) { 1197 return false; 1198 } 1199 Message msg = sm.obtainMessage(HeadsetClientStateMachine.EXPLICIT_CALL_TRANSFER); 1200 sm.sendMessage(msg); 1201 return true; 1202 } 1203 1204 /** Send vendor AT command. */ sendVendorAtCommand(BluetoothDevice device, int vendorId, String atCommand)1205 public boolean sendVendorAtCommand(BluetoothDevice device, int vendorId, String atCommand) { 1206 HeadsetClientStateMachine sm = getStateMachine(device); 1207 if (sm == null) { 1208 Log.e(TAG, "SM does not exist for device " + device); 1209 return false; 1210 } 1211 1212 int connectionState = sm.getConnectionState(device); 1213 if (connectionState != BluetoothProfile.STATE_CONNECTED) { 1214 return false; 1215 } 1216 1217 Message msg = sm.obtainMessage(HeadsetClientStateMachine.SEND_VENDOR_AT_COMMAND, 1218 vendorId, 0, atCommand); 1219 sm.sendMessage(msg); 1220 return true; 1221 } 1222 getCurrentAgEvents(BluetoothDevice device)1223 public Bundle getCurrentAgEvents(BluetoothDevice device) { 1224 HeadsetClientStateMachine sm = getStateMachine(device); 1225 if (sm == null) { 1226 Log.e(TAG, "SM does not exist for device " + device); 1227 return null; 1228 } 1229 1230 int connectionState = sm.getConnectionState(device); 1231 if (connectionState != BluetoothProfile.STATE_CONNECTED) { 1232 return null; 1233 } 1234 return sm.getCurrentAgEvents(); 1235 } 1236 getCurrentAgFeaturesBundle(BluetoothDevice device)1237 public Bundle getCurrentAgFeaturesBundle(BluetoothDevice device) { 1238 HeadsetClientStateMachine sm = getStateMachine(device); 1239 if (sm == null) { 1240 Log.e(TAG, "SM does not exist for device " + device); 1241 return null; 1242 } 1243 int connectionState = sm.getConnectionState(device); 1244 if (connectionState != BluetoothProfile.STATE_CONNECTED) { 1245 return null; 1246 } 1247 return sm.getCurrentAgFeaturesBundle(); 1248 } 1249 getCurrentAgFeatures(BluetoothDevice device)1250 public Set<Integer> getCurrentAgFeatures(BluetoothDevice device) { 1251 HeadsetClientStateMachine sm = getStateMachine(device); 1252 if (sm == null) { 1253 Log.e(TAG, "SM does not exist for device " + device); 1254 return null; 1255 } 1256 int connectionState = sm.getConnectionState(device); 1257 if (connectionState != BluetoothProfile.STATE_CONNECTED) { 1258 return null; 1259 } 1260 return sm.getCurrentAgFeatures(); 1261 } 1262 1263 // Handle messages from native (JNI) to java messageFromNative(StackEvent stackEvent)1264 public void messageFromNative(StackEvent stackEvent) { 1265 Objects.requireNonNull(stackEvent.device, 1266 "Device should never be null, event: " + stackEvent); 1267 1268 HeadsetClientStateMachine sm = getStateMachine(stackEvent.device, 1269 isConnectionEvent(stackEvent)); 1270 if (sm == null) { 1271 throw new IllegalStateException( 1272 "State machine not found for stack event: " + stackEvent); 1273 } 1274 sm.sendMessage(StackEvent.STACK_EVENT, stackEvent); 1275 } 1276 isConnectionEvent(StackEvent stackEvent)1277 private boolean isConnectionEvent(StackEvent stackEvent) { 1278 if (stackEvent.type == StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED) { 1279 if ((stackEvent.valueInt == HeadsetClientHalConstants.CONNECTION_STATE_CONNECTING) 1280 || (stackEvent.valueInt 1281 == HeadsetClientHalConstants.CONNECTION_STATE_CONNECTED)) { 1282 return true; 1283 } 1284 } 1285 return false; 1286 } 1287 getStateMachine(BluetoothDevice device)1288 private HeadsetClientStateMachine getStateMachine(BluetoothDevice device) { 1289 return getStateMachine(device, false); 1290 } 1291 getStateMachine(BluetoothDevice device, boolean isConnectionEvent)1292 private HeadsetClientStateMachine getStateMachine(BluetoothDevice device, 1293 boolean isConnectionEvent) { 1294 if (device == null) { 1295 Log.e(TAG, "getStateMachine failed: Device cannot be null"); 1296 return null; 1297 } 1298 1299 HeadsetClientStateMachine sm; 1300 synchronized (mStateMachineMap) { 1301 sm = mStateMachineMap.get(device); 1302 } 1303 1304 if (sm != null) { 1305 if (DBG) { 1306 Log.d(TAG, "Found SM for device " + device); 1307 } 1308 } else if (isConnectionEvent) { 1309 // The only time a new state machine should be created when none was found is for 1310 // connection events. 1311 sm = allocateStateMachine(device); 1312 if (sm == null) { 1313 Log.e(TAG, "SM could not be allocated for device " + device); 1314 } 1315 } 1316 return sm; 1317 } 1318 allocateStateMachine(BluetoothDevice device)1319 private HeadsetClientStateMachine allocateStateMachine(BluetoothDevice device) { 1320 if (device == null) { 1321 Log.e(TAG, "allocateStateMachine failed: Device cannot be null"); 1322 return null; 1323 } 1324 1325 if (getHeadsetClientService() == null) { 1326 // Preconditions: {@code setHeadsetClientService(this)} is the last thing {@code start} 1327 // does, and {@code setHeadsetClientService(null)} is (one of) the first thing 1328 // {@code stop does}. 1329 Log.e(TAG, "Cannot allocate SM if service has begun stopping or has not completed" 1330 + " startup."); 1331 return null; 1332 } 1333 1334 synchronized (mStateMachineMap) { 1335 HeadsetClientStateMachine sm = mStateMachineMap.get(device); 1336 if (sm != null) { 1337 if (DBG) { 1338 Log.d(TAG, "allocateStateMachine: SM already exists for device " + device); 1339 } 1340 return sm; 1341 } 1342 1343 // There is a possibility of a DOS attack if someone populates here with a lot of fake 1344 // BluetoothAddresses. If it so happens instead of blowing up we can at least put a 1345 // limit on how long the attack would survive 1346 if (mStateMachineMap.keySet().size() > MAX_STATE_MACHINES_POSSIBLE) { 1347 Log.e(TAG, "Max state machines reached, possible DOS attack " 1348 + MAX_STATE_MACHINES_POSSIBLE); 1349 return null; 1350 } 1351 1352 // Allocate a new SM 1353 Log.d(TAG, "Creating a new state machine"); 1354 sm = mSmFactory.make(this, mSmThread, mNativeInterface); 1355 mStateMachineMap.put(device, sm); 1356 return sm; 1357 } 1358 } 1359 1360 // Check if any of the state machines have routed the SCO audio stream. isScoRouted()1361 boolean isScoRouted() { 1362 synchronized (mStateMachineMap) { 1363 for (Map.Entry<BluetoothDevice, HeadsetClientStateMachine> entry : mStateMachineMap 1364 .entrySet()) { 1365 if (entry.getValue() != null) { 1366 int audioState = entry.getValue().getAudioState(entry.getKey()); 1367 if (audioState == HeadsetClientHalConstants.AUDIO_STATE_CONNECTED) { 1368 if (DBG) { 1369 Log.d(TAG, "Device " + entry.getKey() + " audio state " + audioState 1370 + " Connected"); 1371 } 1372 return true; 1373 } 1374 } 1375 } 1376 } 1377 return false; 1378 } 1379 1380 @Override dump(StringBuilder sb)1381 public void dump(StringBuilder sb) { 1382 super.dump(sb); 1383 synchronized (mStateMachineMap) { 1384 for (HeadsetClientStateMachine sm : mStateMachineMap.values()) { 1385 if (sm != null) { 1386 sm.dump(sb); 1387 } 1388 } 1389 1390 sb.append("\n"); 1391 HfpClientConnectionService.dump(sb); 1392 } 1393 } 1394 1395 // For testing getStateMachineMap()1396 protected Map<BluetoothDevice, HeadsetClientStateMachine> getStateMachineMap() { 1397 synchronized (mStateMachineMap) { 1398 return mStateMachineMap; 1399 } 1400 } 1401 setSMFactory(HeadsetClientStateMachineFactory factory)1402 protected void setSMFactory(HeadsetClientStateMachineFactory factory) { 1403 mSmFactory = factory; 1404 } 1405 getAudioManager()1406 protected AudioManager getAudioManager() { 1407 return mAudioManager; 1408 } 1409 updateBatteryLevel()1410 protected void updateBatteryLevel() { 1411 int batteryLevel = mBatteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY); 1412 int batteryIndicatorID = 2; 1413 1414 synchronized (mStateMachineMap) { 1415 for (HeadsetClientStateMachine sm : mStateMachineMap.values()) { 1416 if (sm != null) { 1417 sm.sendMessage(HeadsetClientStateMachine.SEND_BIEV, 1418 batteryIndicatorID, 1419 batteryLevel); 1420 } 1421 } 1422 } 1423 } 1424 } 1425