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