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