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