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