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.bluetooth.BluetoothDevice; 20 import android.bluetooth.BluetoothHeadsetClient; 21 import android.bluetooth.BluetoothHeadsetClientCall; 22 import android.bluetooth.BluetoothProfile; 23 import android.bluetooth.IBluetoothHeadsetClient; 24 import android.content.BroadcastReceiver; 25 import android.content.Context; 26 import android.content.Intent; 27 import android.content.IntentFilter; 28 import android.media.AudioManager; 29 import android.os.Bundle; 30 import android.os.HandlerThread; 31 import android.os.Message; 32 import android.util.Log; 33 34 import com.android.bluetooth.Utils; 35 import com.android.bluetooth.btservice.AdapterService; 36 import com.android.bluetooth.btservice.ProfileService; 37 import com.android.bluetooth.btservice.storage.DatabaseManager; 38 import com.android.bluetooth.hfpclient.connserv.HfpClientConnectionService; 39 40 import java.util.ArrayList; 41 import java.util.HashMap; 42 import java.util.Iterator; 43 import java.util.List; 44 import java.util.Map; 45 import java.util.Objects; 46 import java.util.UUID; 47 48 /** 49 * Provides Bluetooth Headset Client (HF Role) profile, as a service in the 50 * Bluetooth application. 51 * 52 * @hide 53 */ 54 public class HeadsetClientService extends ProfileService { 55 private static final boolean DBG = false; 56 private static final String TAG = "HeadsetClientService"; 57 58 private HashMap<BluetoothDevice, HeadsetClientStateMachine> mStateMachineMap = new HashMap<>(); 59 private static HeadsetClientService sHeadsetClientService; 60 private NativeInterface mNativeInterface = null; 61 private HandlerThread mSmThread = null; 62 private HeadsetClientStateMachineFactory mSmFactory = null; 63 private DatabaseManager mDatabaseManager; 64 private AudioManager mAudioManager = null; 65 // Maxinum number of devices we can try connecting to in one session 66 private static final int MAX_STATE_MACHINES_POSSIBLE = 100; 67 68 public static final String HFP_CLIENT_STOP_TAG = "hfp_client_stop_tag"; 69 70 @Override initBinder()71 public IProfileServiceBinder initBinder() { 72 return new BluetoothHeadsetClientBinder(this); 73 } 74 75 @Override start()76 protected synchronized boolean start() { 77 if (DBG) { 78 Log.d(TAG, "start()"); 79 } 80 if (sHeadsetClientService != null) { 81 Log.w(TAG, "start(): start called without stop"); 82 return false; 83 } 84 85 mDatabaseManager = Objects.requireNonNull(AdapterService.getAdapterService().getDatabase(), 86 "DatabaseManager cannot be null when HeadsetClientService starts"); 87 88 // Setup the JNI service 89 mNativeInterface = NativeInterface.getInstance(); 90 mNativeInterface.initialize(); 91 92 mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); 93 if (mAudioManager == null) { 94 Log.e(TAG, "AudioManager service doesn't exist?"); 95 } else { 96 // start AudioManager in a known state 97 mAudioManager.setParameters("hfp_enable=false"); 98 } 99 100 mSmFactory = new HeadsetClientStateMachineFactory(); 101 mStateMachineMap.clear(); 102 103 IntentFilter filter = new IntentFilter(AudioManager.VOLUME_CHANGED_ACTION); 104 registerReceiver(mBroadcastReceiver, filter); 105 106 // Start the HfpClientConnectionService to create connection with telecom when HFP 107 // connection is available. 108 Intent startIntent = new Intent(this, HfpClientConnectionService.class); 109 startService(startIntent); 110 111 // Create the thread on which all State Machines will run 112 mSmThread = new HandlerThread("HeadsetClient.SM"); 113 mSmThread.start(); 114 115 setHeadsetClientService(this); 116 return true; 117 } 118 119 @Override stop()120 protected synchronized boolean stop() { 121 if (sHeadsetClientService == null) { 122 Log.w(TAG, "stop() called without start()"); 123 return false; 124 } 125 126 // Stop the HfpClientConnectionService. 127 Intent stopIntent = new Intent(this, HfpClientConnectionService.class); 128 sHeadsetClientService.stopService(stopIntent); 129 130 setHeadsetClientService(null); 131 132 unregisterReceiver(mBroadcastReceiver); 133 134 for (Iterator<Map.Entry<BluetoothDevice, HeadsetClientStateMachine>> it = 135 mStateMachineMap.entrySet().iterator(); it.hasNext(); ) { 136 HeadsetClientStateMachine sm = 137 mStateMachineMap.get((BluetoothDevice) it.next().getKey()); 138 sm.doQuit(); 139 it.remove(); 140 } 141 142 // Stop the handler thread 143 mSmThread.quit(); 144 mSmThread = null; 145 146 mNativeInterface.cleanup(); 147 mNativeInterface = null; 148 149 return true; 150 } 151 152 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 153 @Override 154 public void onReceive(Context context, Intent intent) { 155 String action = intent.getAction(); 156 157 // We handle the volume changes for Voice calls here since HFP audio volume control does 158 // not go through audio manager (audio mixer). see 159 // ({@link HeadsetClientStateMachine#SET_SPEAKER_VOLUME} in 160 // {@link HeadsetClientStateMachine} for details. 161 if (action.equals(AudioManager.VOLUME_CHANGED_ACTION)) { 162 if (DBG) { 163 Log.d(TAG, "Volume changed for stream: " + intent.getExtra( 164 AudioManager.EXTRA_VOLUME_STREAM_TYPE)); 165 } 166 int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1); 167 if (streamType == AudioManager.STREAM_VOICE_CALL) { 168 int streamValue = 169 intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, -1); 170 int hfVol = HeadsetClientStateMachine.amToHfVol(streamValue); 171 if (DBG) { 172 Log.d(TAG, 173 "Setting volume to audio manager: " + streamValue + " hands free: " 174 + hfVol); 175 } 176 mAudioManager.setParameters("hfp_volume=" + hfVol); 177 synchronized (this) { 178 for (HeadsetClientStateMachine sm : mStateMachineMap.values()) { 179 if (sm != null) { 180 sm.sendMessage(HeadsetClientStateMachine.SET_SPEAKER_VOLUME, 181 streamValue); 182 } 183 } 184 } 185 } 186 } 187 } 188 }; 189 190 /** 191 * Handlers for incoming service calls 192 */ 193 private static class BluetoothHeadsetClientBinder extends IBluetoothHeadsetClient.Stub 194 implements IProfileServiceBinder { 195 private HeadsetClientService mService; 196 BluetoothHeadsetClientBinder(HeadsetClientService svc)197 BluetoothHeadsetClientBinder(HeadsetClientService svc) { 198 mService = svc; 199 } 200 201 @Override cleanup()202 public void cleanup() { 203 mService = null; 204 } 205 getService()206 private HeadsetClientService getService() { 207 if (!Utils.checkCaller()) { 208 Log.w(TAG, "HeadsetClient call not allowed for non-active user"); 209 return null; 210 } 211 212 if (mService != null && mService.isAvailable()) { 213 return mService; 214 } 215 216 Log.e(TAG, "HeadsetClientService is not available."); 217 return null; 218 } 219 220 @Override connect(BluetoothDevice device)221 public boolean connect(BluetoothDevice device) { 222 HeadsetClientService service = getService(); 223 if (service == null) { 224 return false; 225 } 226 return service.connect(device); 227 } 228 229 @Override disconnect(BluetoothDevice device)230 public boolean disconnect(BluetoothDevice device) { 231 HeadsetClientService service = getService(); 232 if (service == null) { 233 return false; 234 } 235 return service.disconnect(device); 236 } 237 238 @Override getConnectedDevices()239 public List<BluetoothDevice> getConnectedDevices() { 240 HeadsetClientService service = getService(); 241 if (service == null) { 242 return new ArrayList<BluetoothDevice>(0); 243 } 244 return service.getConnectedDevices(); 245 } 246 247 @Override getDevicesMatchingConnectionStates(int[] states)248 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 249 HeadsetClientService service = getService(); 250 if (service == null) { 251 return new ArrayList<BluetoothDevice>(0); 252 } 253 return service.getDevicesMatchingConnectionStates(states); 254 } 255 256 @Override getConnectionState(BluetoothDevice device)257 public int getConnectionState(BluetoothDevice device) { 258 HeadsetClientService service = getService(); 259 if (service == null) { 260 return BluetoothProfile.STATE_DISCONNECTED; 261 } 262 return service.getConnectionState(device); 263 } 264 265 @Override setConnectionPolicy(BluetoothDevice device, int connectionPolicy)266 public boolean setConnectionPolicy(BluetoothDevice device, int connectionPolicy) { 267 HeadsetClientService service = getService(); 268 if (service == null) { 269 return false; 270 } 271 return service.setConnectionPolicy(device, connectionPolicy); 272 } 273 274 @Override getConnectionPolicy(BluetoothDevice device)275 public int getConnectionPolicy(BluetoothDevice device) { 276 HeadsetClientService service = getService(); 277 if (service == null) { 278 return BluetoothProfile.CONNECTION_POLICY_UNKNOWN; 279 } 280 return service.getConnectionPolicy(device); 281 } 282 283 @Override startVoiceRecognition(BluetoothDevice device)284 public boolean startVoiceRecognition(BluetoothDevice device) { 285 HeadsetClientService service = getService(); 286 if (service == null) { 287 return false; 288 } 289 return service.startVoiceRecognition(device); 290 } 291 292 @Override stopVoiceRecognition(BluetoothDevice device)293 public boolean stopVoiceRecognition(BluetoothDevice device) { 294 HeadsetClientService service = getService(); 295 if (service == null) { 296 return false; 297 } 298 return service.stopVoiceRecognition(device); 299 } 300 301 @Override getAudioState(BluetoothDevice device)302 public int getAudioState(BluetoothDevice device) { 303 HeadsetClientService service = getService(); 304 if (service == null) { 305 return BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED; 306 } 307 return service.getAudioState(device); 308 } 309 310 @Override setAudioRouteAllowed(BluetoothDevice device, boolean allowed)311 public void setAudioRouteAllowed(BluetoothDevice device, boolean allowed) { 312 Log.e(TAG, "setAudioRouteAllowed API not supported"); 313 } 314 315 @Override getAudioRouteAllowed(BluetoothDevice device)316 public boolean getAudioRouteAllowed(BluetoothDevice device) { 317 Log.e(TAG, "getAudioRouteAllowed API not supported"); 318 return false; 319 } 320 321 @Override connectAudio(BluetoothDevice device)322 public boolean connectAudio(BluetoothDevice device) { 323 HeadsetClientService service = getService(); 324 if (service == null) { 325 return false; 326 } 327 return service.connectAudio(device); 328 } 329 330 @Override disconnectAudio(BluetoothDevice device)331 public boolean disconnectAudio(BluetoothDevice device) { 332 HeadsetClientService service = getService(); 333 if (service == null) { 334 return false; 335 } 336 return service.disconnectAudio(device); 337 } 338 339 @Override acceptCall(BluetoothDevice device, int flag)340 public boolean acceptCall(BluetoothDevice device, int flag) { 341 HeadsetClientService service = getService(); 342 if (service == null) { 343 return false; 344 } 345 return service.acceptCall(device, flag); 346 } 347 348 @Override rejectCall(BluetoothDevice device)349 public boolean rejectCall(BluetoothDevice device) { 350 HeadsetClientService service = getService(); 351 if (service == null) { 352 return false; 353 } 354 return service.rejectCall(device); 355 } 356 357 @Override holdCall(BluetoothDevice device)358 public boolean holdCall(BluetoothDevice device) { 359 HeadsetClientService service = getService(); 360 if (service == null) { 361 return false; 362 } 363 return service.holdCall(device); 364 } 365 366 @Override terminateCall(BluetoothDevice device, BluetoothHeadsetClientCall call)367 public boolean terminateCall(BluetoothDevice device, BluetoothHeadsetClientCall call) { 368 HeadsetClientService service = getService(); 369 if (service == null) { 370 Log.w(TAG, "service is null"); 371 return false; 372 } 373 return service.terminateCall(device, call != null ? call.getUUID() : null); 374 } 375 376 @Override explicitCallTransfer(BluetoothDevice device)377 public boolean explicitCallTransfer(BluetoothDevice device) { 378 HeadsetClientService service = getService(); 379 if (service == null) { 380 return false; 381 } 382 return service.explicitCallTransfer(device); 383 } 384 385 @Override enterPrivateMode(BluetoothDevice device, int index)386 public boolean enterPrivateMode(BluetoothDevice device, int index) { 387 HeadsetClientService service = getService(); 388 if (service == null) { 389 return false; 390 } 391 return service.enterPrivateMode(device, index); 392 } 393 394 @Override dial(BluetoothDevice device, String number)395 public BluetoothHeadsetClientCall dial(BluetoothDevice device, String number) { 396 HeadsetClientService service = getService(); 397 if (service == null) { 398 return null; 399 } 400 return service.dial(device, number); 401 } 402 403 @Override getCurrentCalls(BluetoothDevice device)404 public List<BluetoothHeadsetClientCall> getCurrentCalls(BluetoothDevice device) { 405 HeadsetClientService service = getService(); 406 if (service == null) { 407 return new ArrayList<BluetoothHeadsetClientCall>(); 408 } 409 return service.getCurrentCalls(device); 410 } 411 412 @Override sendDTMF(BluetoothDevice device, byte code)413 public boolean sendDTMF(BluetoothDevice device, byte code) { 414 HeadsetClientService service = getService(); 415 if (service == null) { 416 return false; 417 } 418 return service.sendDTMF(device, code); 419 } 420 421 @Override getLastVoiceTagNumber(BluetoothDevice device)422 public boolean getLastVoiceTagNumber(BluetoothDevice device) { 423 HeadsetClientService service = getService(); 424 if (service == null) { 425 return false; 426 } 427 return service.getLastVoiceTagNumber(device); 428 } 429 430 @Override getCurrentAgEvents(BluetoothDevice device)431 public Bundle getCurrentAgEvents(BluetoothDevice device) { 432 HeadsetClientService service = getService(); 433 if (service == null) { 434 return null; 435 } 436 return service.getCurrentAgEvents(device); 437 } 438 439 @Override sendVendorAtCommand(BluetoothDevice device, int vendorId, String atCommand)440 public boolean sendVendorAtCommand(BluetoothDevice device, int vendorId, String atCommand) { 441 HeadsetClientService service = getService(); 442 if (service == null) { 443 return false; 444 } 445 return service.sendVendorAtCommand(device, vendorId, atCommand); 446 } 447 448 @Override getCurrentAgFeatures(BluetoothDevice device)449 public Bundle getCurrentAgFeatures(BluetoothDevice device) { 450 HeadsetClientService service = getService(); 451 if (service == null) { 452 return null; 453 } 454 return service.getCurrentAgFeatures(device); 455 } 456 } 457 458 ; 459 460 // API methods getHeadsetClientService()461 public static synchronized HeadsetClientService getHeadsetClientService() { 462 if (sHeadsetClientService == null) { 463 Log.w(TAG, "getHeadsetClientService(): service is null"); 464 return null; 465 } 466 if (!sHeadsetClientService.isAvailable()) { 467 Log.w(TAG, "getHeadsetClientService(): service is not available "); 468 return null; 469 } 470 return sHeadsetClientService; 471 } 472 setHeadsetClientService(HeadsetClientService instance)473 private static synchronized void setHeadsetClientService(HeadsetClientService instance) { 474 if (DBG) { 475 Log.d(TAG, "setHeadsetClientService(): set to: " + instance); 476 } 477 sHeadsetClientService = instance; 478 } 479 connect(BluetoothDevice device)480 public boolean connect(BluetoothDevice device) { 481 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); 482 if (DBG) { 483 Log.d(TAG, "connect " + device); 484 } 485 HeadsetClientStateMachine sm = getStateMachine(device); 486 if (sm == null) { 487 Log.e(TAG, "Cannot allocate SM for device " + device); 488 return false; 489 } 490 491 if (getConnectionPolicy(device) == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) { 492 Log.w(TAG, "Connection not allowed: <" + device.getAddress() 493 + "> is CONNECTION_POLICY_FORBIDDEN"); 494 return false; 495 } 496 497 sm.sendMessage(HeadsetClientStateMachine.CONNECT, device); 498 return true; 499 } 500 501 /** 502 * Disconnects hfp client for the remote bluetooth device 503 * 504 * @param device is the device with which we are attempting to disconnect the profile 505 * @return true if hfp client profile successfully disconnected, false otherwise 506 */ disconnect(BluetoothDevice device)507 public boolean disconnect(BluetoothDevice device) { 508 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); 509 HeadsetClientStateMachine sm = getStateMachine(device); 510 if (sm == null) { 511 Log.e(TAG, "Cannot allocate SM for device " + device); 512 return false; 513 } 514 515 int connectionState = sm.getConnectionState(device); 516 if (connectionState != BluetoothProfile.STATE_CONNECTED 517 && connectionState != BluetoothProfile.STATE_CONNECTING) { 518 return false; 519 } 520 521 sm.sendMessage(HeadsetClientStateMachine.DISCONNECT, device); 522 return true; 523 } 524 getConnectedDevices()525 public synchronized List<BluetoothDevice> getConnectedDevices() { 526 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 527 528 ArrayList<BluetoothDevice> connectedDevices = new ArrayList<>(); 529 for (BluetoothDevice bd : mStateMachineMap.keySet()) { 530 HeadsetClientStateMachine sm = mStateMachineMap.get(bd); 531 if (sm != null && sm.getConnectionState(bd) == BluetoothProfile.STATE_CONNECTED) { 532 connectedDevices.add(bd); 533 } 534 } 535 return connectedDevices; 536 } 537 getDevicesMatchingConnectionStates(int[] states)538 private synchronized List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 539 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 540 List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>(); 541 for (BluetoothDevice bd : mStateMachineMap.keySet()) { 542 for (int state : states) { 543 HeadsetClientStateMachine sm = mStateMachineMap.get(bd); 544 if (sm != null && sm.getConnectionState(bd) == state) { 545 devices.add(bd); 546 } 547 } 548 } 549 return devices; 550 } 551 552 /** 553 * Get the current connection state of the profile 554 * 555 * @param device is the remote bluetooth device 556 * @return {@link BluetoothProfile#STATE_DISCONNECTED} if this profile is disconnected, 557 * {@link BluetoothProfile#STATE_CONNECTING} if this profile is being connected, 558 * {@link BluetoothProfile#STATE_CONNECTED} if this profile is connected, or 559 * {@link BluetoothProfile#STATE_DISCONNECTING} if this profile is being disconnected 560 */ getConnectionState(BluetoothDevice device)561 public synchronized int getConnectionState(BluetoothDevice device) { 562 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 563 HeadsetClientStateMachine sm = mStateMachineMap.get(device); 564 if (sm != null) { 565 return sm.getConnectionState(device); 566 } 567 return BluetoothProfile.STATE_DISCONNECTED; 568 } 569 570 /** 571 * Set connection policy of the profile and connects it if connectionPolicy is 572 * {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED} or disconnects if connectionPolicy is 573 * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN} 574 * 575 * <p> The device should already be paired. 576 * Connection policy can be one of: 577 * {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED}, 578 * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN}, 579 * {@link BluetoothProfile#CONNECTION_POLICY_UNKNOWN} 580 * 581 * @param device Paired bluetooth device 582 * @param connectionPolicy is the connection policy to set to for this profile 583 * @return true if connectionPolicy is set, false on error 584 */ setConnectionPolicy(BluetoothDevice device, int connectionPolicy)585 public boolean setConnectionPolicy(BluetoothDevice device, int connectionPolicy) { 586 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); 587 if (DBG) { 588 Log.d(TAG, "Saved connectionPolicy " + device + " = " + connectionPolicy); 589 } 590 591 if (!mDatabaseManager.setProfileConnectionPolicy(device, BluetoothProfile.HEADSET_CLIENT, 592 connectionPolicy)) { 593 return false; 594 } 595 if (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED) { 596 connect(device); 597 } else if (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) { 598 disconnect(device); 599 } 600 return true; 601 } 602 603 /** 604 * Get the connection policy of the profile. 605 * 606 * <p> The connection policy can be any of: 607 * {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED}, 608 * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN}, 609 * {@link BluetoothProfile#CONNECTION_POLICY_UNKNOWN} 610 * 611 * @param device Bluetooth device 612 * @return connection policy of the device 613 * @hide 614 */ getConnectionPolicy(BluetoothDevice device)615 public int getConnectionPolicy(BluetoothDevice device) { 616 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); 617 return mDatabaseManager 618 .getProfileConnectionPolicy(device, BluetoothProfile.HEADSET_CLIENT); 619 } 620 startVoiceRecognition(BluetoothDevice device)621 boolean startVoiceRecognition(BluetoothDevice device) { 622 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 623 HeadsetClientStateMachine sm = getStateMachine(device); 624 if (sm == null) { 625 Log.e(TAG, "Cannot allocate SM for device " + device); 626 return false; 627 } 628 int connectionState = sm.getConnectionState(device); 629 if (connectionState != BluetoothProfile.STATE_CONNECTED) { 630 return false; 631 } 632 sm.sendMessage(HeadsetClientStateMachine.VOICE_RECOGNITION_START); 633 return true; 634 } 635 stopVoiceRecognition(BluetoothDevice device)636 boolean stopVoiceRecognition(BluetoothDevice device) { 637 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 638 HeadsetClientStateMachine sm = getStateMachine(device); 639 if (sm == null) { 640 Log.e(TAG, "Cannot allocate SM for device " + device); 641 return false; 642 } 643 int connectionState = sm.getConnectionState(device); 644 if (connectionState != BluetoothProfile.STATE_CONNECTED) { 645 return false; 646 } 647 sm.sendMessage(HeadsetClientStateMachine.VOICE_RECOGNITION_STOP); 648 return true; 649 } 650 getAudioState(BluetoothDevice device)651 int getAudioState(BluetoothDevice device) { 652 HeadsetClientStateMachine sm = getStateMachine(device); 653 if (sm == null) { 654 Log.e(TAG, "Cannot allocate SM for device " + device); 655 return -1; 656 } 657 658 return sm.getAudioState(device); 659 } 660 connectAudio(BluetoothDevice device)661 boolean connectAudio(BluetoothDevice device) { 662 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); 663 HeadsetClientStateMachine sm = getStateMachine(device); 664 if (sm == null) { 665 Log.e(TAG, "Cannot allocate SM for device " + device); 666 return false; 667 } 668 669 if (!sm.isConnected()) { 670 return false; 671 } 672 if (sm.isAudioOn()) { 673 return false; 674 } 675 sm.sendMessage(HeadsetClientStateMachine.CONNECT_AUDIO); 676 return true; 677 } 678 disconnectAudio(BluetoothDevice device)679 boolean disconnectAudio(BluetoothDevice device) { 680 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); 681 HeadsetClientStateMachine sm = getStateMachine(device); 682 if (sm == null) { 683 Log.e(TAG, "Cannot allocate SM for device " + device); 684 return false; 685 } 686 687 if (!sm.isAudioOn()) { 688 return false; 689 } 690 sm.sendMessage(HeadsetClientStateMachine.DISCONNECT_AUDIO); 691 return true; 692 } 693 holdCall(BluetoothDevice device)694 boolean holdCall(BluetoothDevice device) { 695 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 696 HeadsetClientStateMachine sm = getStateMachine(device); 697 if (sm == null) { 698 Log.e(TAG, "Cannot allocate SM for device " + device); 699 return false; 700 } 701 702 int connectionState = sm.getConnectionState(device); 703 if (connectionState != BluetoothProfile.STATE_CONNECTED 704 && connectionState != BluetoothProfile.STATE_CONNECTING) { 705 return false; 706 } 707 Message msg = sm.obtainMessage(HeadsetClientStateMachine.HOLD_CALL); 708 sm.sendMessage(msg); 709 return true; 710 } 711 acceptCall(BluetoothDevice device, int flag)712 boolean acceptCall(BluetoothDevice device, int flag) { 713 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 714 /* Phonecalls from a single device are supported, hang up any calls on the other phone */ 715 synchronized (this) { 716 for (Map.Entry<BluetoothDevice, HeadsetClientStateMachine> entry : mStateMachineMap 717 .entrySet()) { 718 if (entry.getValue() == null || entry.getKey().equals(device)) { 719 continue; 720 } 721 int connectionState = entry.getValue().getConnectionState(entry.getKey()); 722 if (DBG) { 723 Log.d(TAG, 724 "Accepting a call on device " + device + ". Possibly disconnecting on " 725 + entry.getValue()); 726 } 727 if (connectionState == BluetoothProfile.STATE_CONNECTED) { 728 entry.getValue() 729 .obtainMessage(HeadsetClientStateMachine.TERMINATE_CALL) 730 .sendToTarget(); 731 } 732 } 733 } 734 HeadsetClientStateMachine sm = getStateMachine(device); 735 if (sm == null) { 736 Log.e(TAG, "Cannot allocate SM for device " + device); 737 return false; 738 } 739 740 int connectionState = sm.getConnectionState(device); 741 if (connectionState != BluetoothProfile.STATE_CONNECTED) { 742 return false; 743 } 744 Message msg = sm.obtainMessage(HeadsetClientStateMachine.ACCEPT_CALL); 745 msg.arg1 = flag; 746 sm.sendMessage(msg); 747 return true; 748 } 749 rejectCall(BluetoothDevice device)750 boolean rejectCall(BluetoothDevice device) { 751 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 752 HeadsetClientStateMachine sm = getStateMachine(device); 753 if (sm == null) { 754 Log.e(TAG, "Cannot allocate SM for device " + device); 755 return false; 756 } 757 758 int connectionState = sm.getConnectionState(device); 759 if (connectionState != BluetoothProfile.STATE_CONNECTED 760 && connectionState != BluetoothProfile.STATE_CONNECTING) { 761 return false; 762 } 763 764 Message msg = sm.obtainMessage(HeadsetClientStateMachine.REJECT_CALL); 765 sm.sendMessage(msg); 766 return true; 767 } 768 terminateCall(BluetoothDevice device, UUID uuid)769 boolean terminateCall(BluetoothDevice device, UUID uuid) { 770 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 771 HeadsetClientStateMachine sm = getStateMachine(device); 772 if (sm == null) { 773 Log.e(TAG, "Cannot allocate SM for device " + device); 774 return false; 775 } 776 777 int connectionState = sm.getConnectionState(device); 778 if (connectionState != BluetoothProfile.STATE_CONNECTED 779 && connectionState != BluetoothProfile.STATE_CONNECTING) { 780 return false; 781 } 782 783 Message msg = sm.obtainMessage(HeadsetClientStateMachine.TERMINATE_CALL); 784 msg.obj = uuid; 785 sm.sendMessage(msg); 786 return true; 787 } 788 enterPrivateMode(BluetoothDevice device, int index)789 boolean enterPrivateMode(BluetoothDevice device, int index) { 790 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 791 HeadsetClientStateMachine sm = getStateMachine(device); 792 if (sm == null) { 793 Log.e(TAG, "Cannot allocate SM for device " + device); 794 return false; 795 } 796 797 int connectionState = sm.getConnectionState(device); 798 if (connectionState != BluetoothProfile.STATE_CONNECTED 799 && connectionState != BluetoothProfile.STATE_CONNECTING) { 800 return false; 801 } 802 803 Message msg = sm.obtainMessage(HeadsetClientStateMachine.ENTER_PRIVATE_MODE); 804 msg.arg1 = index; 805 sm.sendMessage(msg); 806 return true; 807 } 808 dial(BluetoothDevice device, String number)809 BluetoothHeadsetClientCall dial(BluetoothDevice device, String number) { 810 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 811 HeadsetClientStateMachine sm = getStateMachine(device); 812 if (sm == null) { 813 Log.e(TAG, "Cannot allocate SM for device " + device); 814 return null; 815 } 816 817 int connectionState = sm.getConnectionState(device); 818 if (connectionState != BluetoothProfile.STATE_CONNECTED 819 && connectionState != BluetoothProfile.STATE_CONNECTING) { 820 return null; 821 } 822 823 BluetoothHeadsetClientCall call = new BluetoothHeadsetClientCall(device, 824 HeadsetClientStateMachine.HF_ORIGINATED_CALL_ID, 825 BluetoothHeadsetClientCall.CALL_STATE_DIALING, number, false /* multiparty */, 826 true /* outgoing */, sm.getInBandRing()); 827 Message msg = sm.obtainMessage(HeadsetClientStateMachine.DIAL_NUMBER); 828 msg.obj = call; 829 sm.sendMessage(msg); 830 return call; 831 } 832 sendDTMF(BluetoothDevice device, byte code)833 public boolean sendDTMF(BluetoothDevice device, byte code) { 834 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 835 HeadsetClientStateMachine sm = getStateMachine(device); 836 if (sm == null) { 837 Log.e(TAG, "Cannot allocate SM for device " + device); 838 return false; 839 } 840 841 int connectionState = sm.getConnectionState(device); 842 if (connectionState != BluetoothProfile.STATE_CONNECTED 843 && connectionState != BluetoothProfile.STATE_CONNECTING) { 844 return false; 845 } 846 Message msg = sm.obtainMessage(HeadsetClientStateMachine.SEND_DTMF); 847 msg.arg1 = code; 848 sm.sendMessage(msg); 849 return true; 850 } 851 getLastVoiceTagNumber(BluetoothDevice device)852 public boolean getLastVoiceTagNumber(BluetoothDevice device) { 853 return false; 854 } 855 getCurrentCalls(BluetoothDevice device)856 public List<BluetoothHeadsetClientCall> getCurrentCalls(BluetoothDevice device) { 857 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 858 HeadsetClientStateMachine sm = getStateMachine(device); 859 if (sm == null) { 860 Log.e(TAG, "Cannot allocate SM for device " + device); 861 return null; 862 } 863 864 int connectionState = sm.getConnectionState(device); 865 if (connectionState != BluetoothProfile.STATE_CONNECTED) { 866 return null; 867 } 868 return sm.getCurrentCalls(); 869 } 870 explicitCallTransfer(BluetoothDevice device)871 public boolean explicitCallTransfer(BluetoothDevice device) { 872 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 873 HeadsetClientStateMachine sm = getStateMachine(device); 874 if (sm == null) { 875 Log.e(TAG, "Cannot allocate SM for device " + device); 876 return false; 877 } 878 879 int connectionState = sm.getConnectionState(device); 880 if (connectionState != BluetoothProfile.STATE_CONNECTED 881 && connectionState != BluetoothProfile.STATE_CONNECTING) { 882 return false; 883 } 884 Message msg = sm.obtainMessage(HeadsetClientStateMachine.EXPLICIT_CALL_TRANSFER); 885 sm.sendMessage(msg); 886 return true; 887 } 888 889 /** Send vendor AT command. */ sendVendorAtCommand(BluetoothDevice device, int vendorId, String atCommand)890 public boolean sendVendorAtCommand(BluetoothDevice device, int vendorId, String atCommand) { 891 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 892 HeadsetClientStateMachine sm = getStateMachine(device); 893 if (sm == null) { 894 Log.e(TAG, "Cannot allocate SM for device " + device); 895 return false; 896 } 897 898 int connectionState = sm.getConnectionState(device); 899 if (connectionState != BluetoothProfile.STATE_CONNECTED) { 900 return false; 901 } 902 903 Message msg = sm.obtainMessage(HeadsetClientStateMachine.SEND_VENDOR_AT_COMMAND, 904 vendorId, 0, atCommand); 905 sm.sendMessage(msg); 906 return true; 907 } 908 getCurrentAgEvents(BluetoothDevice device)909 public Bundle getCurrentAgEvents(BluetoothDevice device) { 910 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 911 HeadsetClientStateMachine sm = getStateMachine(device); 912 if (sm == null) { 913 Log.e(TAG, "Cannot allocate SM for device " + device); 914 return null; 915 } 916 917 int connectionState = sm.getConnectionState(device); 918 if (connectionState != BluetoothProfile.STATE_CONNECTED) { 919 return null; 920 } 921 return sm.getCurrentAgEvents(); 922 } 923 getCurrentAgFeatures(BluetoothDevice device)924 public Bundle getCurrentAgFeatures(BluetoothDevice device) { 925 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 926 HeadsetClientStateMachine sm = getStateMachine(device); 927 if (sm == null) { 928 Log.e(TAG, "Cannot allocate SM for device " + device); 929 return null; 930 } 931 int connectionState = sm.getConnectionState(device); 932 if (connectionState != BluetoothProfile.STATE_CONNECTED) { 933 return null; 934 } 935 return sm.getCurrentAgFeatures(); 936 } 937 938 // Handle messages from native (JNI) to java messageFromNative(StackEvent stackEvent)939 public void messageFromNative(StackEvent stackEvent) { 940 HeadsetClientStateMachine sm = getStateMachine(stackEvent.device); 941 if (sm == null) { 942 Log.w(TAG, "No SM found for event " + stackEvent); 943 } 944 945 sm.sendMessage(StackEvent.STACK_EVENT, stackEvent); 946 } 947 948 // State machine management getStateMachine(BluetoothDevice device)949 private synchronized HeadsetClientStateMachine getStateMachine(BluetoothDevice device) { 950 if (device == null) { 951 Log.e(TAG, "getStateMachine failed: Device cannot be null"); 952 return null; 953 } 954 955 HeadsetClientStateMachine sm = mStateMachineMap.get(device); 956 if (sm != null) { 957 if (DBG) { 958 Log.d(TAG, "Found SM for device " + device); 959 } 960 return sm; 961 } 962 963 // There is a possibility of a DOS attack if someone populates here with a lot of fake 964 // BluetoothAddresses. If it so happens instead of blowing up we can atleast put a limit on 965 // how long the attack would survive 966 if (mStateMachineMap.keySet().size() > MAX_STATE_MACHINES_POSSIBLE) { 967 Log.e(TAG, "Max state machines reached, possible DOS attack " 968 + MAX_STATE_MACHINES_POSSIBLE); 969 return null; 970 } 971 972 // Allocate a new SM 973 Log.d(TAG, "Creating a new state machine"); 974 sm = mSmFactory.make(this, mSmThread, mNativeInterface); 975 mStateMachineMap.put(device, sm); 976 return sm; 977 } 978 979 // Check if any of the state machines have routed the SCO audio stream. isScoRouted()980 synchronized boolean isScoRouted() { 981 for (Map.Entry<BluetoothDevice, HeadsetClientStateMachine> entry : mStateMachineMap 982 .entrySet()) { 983 if (entry.getValue() != null) { 984 int audioState = entry.getValue().getAudioState(entry.getKey()); 985 if (audioState == BluetoothHeadsetClient.STATE_AUDIO_CONNECTED) { 986 if (DBG) { 987 Log.d(TAG, "Device " + entry.getKey() + " audio state " + audioState 988 + " Connected"); 989 } 990 return true; 991 } 992 } 993 } 994 return false; 995 } 996 997 @Override dump(StringBuilder sb)998 public synchronized void dump(StringBuilder sb) { 999 super.dump(sb); 1000 for (HeadsetClientStateMachine sm : mStateMachineMap.values()) { 1001 if (sm != null) { 1002 sm.dump(sb); 1003 } 1004 } 1005 } 1006 1007 // For testing getStateMachineMap()1008 protected synchronized Map<BluetoothDevice, HeadsetClientStateMachine> getStateMachineMap() { 1009 return mStateMachineMap; 1010 } 1011 setSMFactory(HeadsetClientStateMachineFactory factory)1012 protected void setSMFactory(HeadsetClientStateMachineFactory factory) { 1013 mSmFactory = factory; 1014 } 1015 getAudioManager()1016 protected AudioManager getAudioManager() { 1017 return mAudioManager; 1018 } 1019 } 1020