HeadsetClientService.java revision bd90909c4ef180602ac088758ffdc13d37d24629
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 106 return true; 107 } 108 109 @Override 110 protected synchronized boolean stop() { 111 try { 112 unregisterReceiver(mBroadcastReceiver); 113 } catch (Exception e) { 114 Log.w(TAG, "Unable to unregister broadcast receiver", e); 115 } 116 117 for (Iterator<Map.Entry<BluetoothDevice, HeadsetClientStateMachine>> it = 118 mStateMachineMap.entrySet().iterator(); it.hasNext(); ) { 119 HeadsetClientStateMachine sm = 120 mStateMachineMap.get((BluetoothDevice) it.next().getKey()); 121 sm.doQuit(); 122 it.remove(); 123 } 124 125 // Stop the HfpClientConnectionService. 126 Intent stopIntent = new Intent(this, HfpClientConnectionService.class); 127 stopIntent.putExtra(HFP_CLIENT_STOP_TAG, true); 128 startService(stopIntent); 129 mNativeInterface = null; 130 131 // Stop the handler thread 132 mSmThread.quit(); 133 mSmThread = null; 134 135 NativeInterface.cleanupNative(); 136 137 return true; 138 } 139 140 @Override 141 protected void cleanup() { 142 HeadsetClientStateMachine.cleanup(); 143 // TODO(b/72948646): should be moved to stop() 144 setHeadsetClientService(null); 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) { 449 Log.w(TAG, "getHeadsetClientService(): service is null"); 450 return null; 451 } 452 if (!sHeadsetClientService.isAvailable()) { 453 Log.w(TAG, "getHeadsetClientService(): service is not available "); 454 return null; 455 } 456 return sHeadsetClientService; 457 } 458 459 private static synchronized void setHeadsetClientService(HeadsetClientService instance) { 460 if (DBG) { 461 Log.d(TAG, "setHeadsetClientService(): set to: " + instance); 462 } 463 sHeadsetClientService = instance; 464 } 465 466 public boolean connect(BluetoothDevice device) { 467 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission"); 468 if (DBG) { 469 Log.d(TAG, "connect " + device); 470 } 471 HeadsetClientStateMachine sm = getStateMachine(device); 472 if (sm == null) { 473 Log.e(TAG, "Cannot allocate SM for device " + device); 474 return false; 475 } 476 477 if (getPriority(device) == BluetoothProfile.PRIORITY_OFF) { 478 Log.w(TAG, "Connection not allowed: <" + device.getAddress() + "> is PRIORITY_OFF"); 479 return false; 480 } 481 482 sm.sendMessage(HeadsetClientStateMachine.CONNECT, device); 483 return true; 484 } 485 486 boolean disconnect(BluetoothDevice device) { 487 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission"); 488 HeadsetClientStateMachine sm = getStateMachine(device); 489 if (sm == null) { 490 Log.e(TAG, "Cannot allocate SM for device " + device); 491 return false; 492 } 493 494 int connectionState = sm.getConnectionState(device); 495 if (connectionState != BluetoothProfile.STATE_CONNECTED 496 && connectionState != BluetoothProfile.STATE_CONNECTING) { 497 return false; 498 } 499 500 sm.sendMessage(HeadsetClientStateMachine.DISCONNECT, device); 501 return true; 502 } 503 504 public synchronized List<BluetoothDevice> getConnectedDevices() { 505 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 506 507 ArrayList<BluetoothDevice> connectedDevices = new ArrayList<>(); 508 for (BluetoothDevice bd : mStateMachineMap.keySet()) { 509 HeadsetClientStateMachine sm = mStateMachineMap.get(bd); 510 if (sm != null && sm.getConnectionState(bd) == BluetoothProfile.STATE_CONNECTED) { 511 connectedDevices.add(bd); 512 } 513 } 514 return connectedDevices; 515 } 516 517 private synchronized List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 518 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 519 List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>(); 520 for (BluetoothDevice bd : mStateMachineMap.keySet()) { 521 for (int state : states) { 522 HeadsetClientStateMachine sm = mStateMachineMap.get(bd); 523 if (sm != null && sm.getConnectionState(bd) == state) { 524 devices.add(bd); 525 } 526 } 527 } 528 return devices; 529 } 530 531 private synchronized int getConnectionState(BluetoothDevice device) { 532 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 533 HeadsetClientStateMachine sm = mStateMachineMap.get(device); 534 if (sm != null) { 535 return sm.getConnectionState(device); 536 } 537 return BluetoothProfile.STATE_DISCONNECTED; 538 } 539 540 public boolean setPriority(BluetoothDevice device, int priority) { 541 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); 542 Settings.Global.putInt(getContentResolver(), 543 Settings.Global.getBluetoothHeadsetPriorityKey(device.getAddress()), priority); 544 if (DBG) { 545 Log.d(TAG, "Saved priority " + device + " = " + priority); 546 } 547 return true; 548 } 549 550 public int getPriority(BluetoothDevice device) { 551 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); 552 int priority = Settings.Global.getInt(getContentResolver(), 553 Settings.Global.getBluetoothHeadsetPriorityKey(device.getAddress()), 554 BluetoothProfile.PRIORITY_UNDEFINED); 555 return priority; 556 } 557 558 boolean startVoiceRecognition(BluetoothDevice device) { 559 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 560 HeadsetClientStateMachine sm = getStateMachine(device); 561 if (sm == null) { 562 Log.e(TAG, "Cannot allocate SM for device " + device); 563 return false; 564 } 565 int connectionState = sm.getConnectionState(device); 566 if (connectionState != BluetoothProfile.STATE_CONNECTED) { 567 return false; 568 } 569 sm.sendMessage(HeadsetClientStateMachine.VOICE_RECOGNITION_START); 570 return true; 571 } 572 573 boolean stopVoiceRecognition(BluetoothDevice device) { 574 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 575 HeadsetClientStateMachine sm = getStateMachine(device); 576 if (sm == null) { 577 Log.e(TAG, "Cannot allocate SM for device " + device); 578 return false; 579 } 580 int connectionState = sm.getConnectionState(device); 581 if (connectionState != BluetoothProfile.STATE_CONNECTED) { 582 return false; 583 } 584 sm.sendMessage(HeadsetClientStateMachine.VOICE_RECOGNITION_STOP); 585 return true; 586 } 587 588 int getAudioState(BluetoothDevice device) { 589 HeadsetClientStateMachine sm = getStateMachine(device); 590 if (sm == null) { 591 Log.e(TAG, "Cannot allocate SM for device " + device); 592 return -1; 593 } 594 595 return sm.getAudioState(device); 596 } 597 598 boolean connectAudio(BluetoothDevice device) { 599 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); 600 HeadsetClientStateMachine sm = getStateMachine(device); 601 if (sm == null) { 602 Log.e(TAG, "Cannot allocate SM for device " + device); 603 return false; 604 } 605 606 if (!sm.isConnected()) { 607 return false; 608 } 609 if (sm.isAudioOn()) { 610 return false; 611 } 612 sm.sendMessage(HeadsetClientStateMachine.CONNECT_AUDIO); 613 return true; 614 } 615 616 boolean disconnectAudio(BluetoothDevice device) { 617 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); 618 HeadsetClientStateMachine sm = getStateMachine(device); 619 if (sm == null) { 620 Log.e(TAG, "Cannot allocate SM for device " + device); 621 return false; 622 } 623 624 if (!sm.isAudioOn()) { 625 return false; 626 } 627 sm.sendMessage(HeadsetClientStateMachine.DISCONNECT_AUDIO); 628 return true; 629 } 630 631 boolean holdCall(BluetoothDevice device) { 632 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 633 HeadsetClientStateMachine sm = getStateMachine(device); 634 if (sm == null) { 635 Log.e(TAG, "Cannot allocate SM for device " + device); 636 return false; 637 } 638 639 int connectionState = sm.getConnectionState(device); 640 if (connectionState != BluetoothProfile.STATE_CONNECTED 641 && connectionState != BluetoothProfile.STATE_CONNECTING) { 642 return false; 643 } 644 Message msg = sm.obtainMessage(HeadsetClientStateMachine.HOLD_CALL); 645 sm.sendMessage(msg); 646 return true; 647 } 648 649 boolean acceptCall(BluetoothDevice device, int flag) { 650 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 651 /* Phonecalls from a single device are supported, hang up any calls on the other phone */ 652 synchronized (this) { 653 for (Map.Entry<BluetoothDevice, HeadsetClientStateMachine> entry : mStateMachineMap 654 .entrySet()) { 655 if (entry.getValue() == null || entry.getKey().equals(device)) { 656 continue; 657 } 658 int connectionState = entry.getValue().getConnectionState(entry.getKey()); 659 if (DBG) { 660 Log.d(TAG, 661 "Accepting a call on device " + device + ". Possibly disconnecting on " 662 + entry.getValue()); 663 } 664 if (connectionState == BluetoothProfile.STATE_CONNECTED) { 665 entry.getValue() 666 .obtainMessage(HeadsetClientStateMachine.TERMINATE_CALL) 667 .sendToTarget(); 668 } 669 } 670 } 671 HeadsetClientStateMachine sm = getStateMachine(device); 672 if (sm == null) { 673 Log.e(TAG, "Cannot allocate SM for device " + device); 674 return false; 675 } 676 677 int connectionState = sm.getConnectionState(device); 678 if (connectionState != BluetoothProfile.STATE_CONNECTED) { 679 return false; 680 } 681 Message msg = sm.obtainMessage(HeadsetClientStateMachine.ACCEPT_CALL); 682 msg.arg1 = flag; 683 sm.sendMessage(msg); 684 return true; 685 } 686 687 boolean rejectCall(BluetoothDevice device) { 688 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 689 HeadsetClientStateMachine sm = getStateMachine(device); 690 if (sm == null) { 691 Log.e(TAG, "Cannot allocate SM for device " + device); 692 return false; 693 } 694 695 int connectionState = sm.getConnectionState(device); 696 if (connectionState != BluetoothProfile.STATE_CONNECTED 697 && connectionState != BluetoothProfile.STATE_CONNECTING) { 698 return false; 699 } 700 701 Message msg = sm.obtainMessage(HeadsetClientStateMachine.REJECT_CALL); 702 sm.sendMessage(msg); 703 return true; 704 } 705 706 boolean terminateCall(BluetoothDevice device, UUID uuid) { 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.TERMINATE_CALL); 721 msg.obj = uuid; 722 sm.sendMessage(msg); 723 return true; 724 } 725 726 boolean enterPrivateMode(BluetoothDevice device, int index) { 727 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 728 HeadsetClientStateMachine sm = getStateMachine(device); 729 if (sm == null) { 730 Log.e(TAG, "Cannot allocate SM for device " + device); 731 return false; 732 } 733 734 int connectionState = sm.getConnectionState(device); 735 if (connectionState != BluetoothProfile.STATE_CONNECTED 736 && connectionState != BluetoothProfile.STATE_CONNECTING) { 737 return false; 738 } 739 740 Message msg = sm.obtainMessage(HeadsetClientStateMachine.ENTER_PRIVATE_MODE); 741 msg.arg1 = index; 742 sm.sendMessage(msg); 743 return true; 744 } 745 746 BluetoothHeadsetClientCall dial(BluetoothDevice device, String number) { 747 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 748 HeadsetClientStateMachine sm = getStateMachine(device); 749 if (sm == null) { 750 Log.e(TAG, "Cannot allocate SM for device " + device); 751 return null; 752 } 753 754 int connectionState = sm.getConnectionState(device); 755 if (connectionState != BluetoothProfile.STATE_CONNECTED 756 && connectionState != BluetoothProfile.STATE_CONNECTING) { 757 return null; 758 } 759 760 BluetoothHeadsetClientCall call = new BluetoothHeadsetClientCall(device, 761 HeadsetClientStateMachine.HF_ORIGINATED_CALL_ID, 762 BluetoothHeadsetClientCall.CALL_STATE_DIALING, number, false /* multiparty */, 763 true /* outgoing */, sm.getInBandRing()); 764 Message msg = sm.obtainMessage(HeadsetClientStateMachine.DIAL_NUMBER); 765 msg.obj = call; 766 sm.sendMessage(msg); 767 return call; 768 } 769 770 public boolean sendDTMF(BluetoothDevice device, byte code) { 771 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 772 HeadsetClientStateMachine sm = getStateMachine(device); 773 if (sm == null) { 774 Log.e(TAG, "Cannot allocate SM for device " + device); 775 return false; 776 } 777 778 int connectionState = sm.getConnectionState(device); 779 if (connectionState != BluetoothProfile.STATE_CONNECTED 780 && connectionState != BluetoothProfile.STATE_CONNECTING) { 781 return false; 782 } 783 Message msg = sm.obtainMessage(HeadsetClientStateMachine.SEND_DTMF); 784 msg.arg1 = code; 785 sm.sendMessage(msg); 786 return true; 787 } 788 789 public boolean getLastVoiceTagNumber(BluetoothDevice device) { 790 return false; 791 } 792 793 public List<BluetoothHeadsetClientCall> getCurrentCalls(BluetoothDevice device) { 794 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 795 HeadsetClientStateMachine sm = getStateMachine(device); 796 if (sm == null) { 797 Log.e(TAG, "Cannot allocate SM for device " + device); 798 return null; 799 } 800 801 int connectionState = sm.getConnectionState(device); 802 if (connectionState != BluetoothProfile.STATE_CONNECTED) { 803 return null; 804 } 805 return sm.getCurrentCalls(); 806 } 807 808 public boolean explicitCallTransfer(BluetoothDevice device) { 809 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 810 HeadsetClientStateMachine sm = getStateMachine(device); 811 if (sm == null) { 812 Log.e(TAG, "Cannot allocate SM for device " + device); 813 return false; 814 } 815 816 int connectionState = sm.getConnectionState(device); 817 if (connectionState != BluetoothProfile.STATE_CONNECTED 818 && connectionState != BluetoothProfile.STATE_CONNECTING) { 819 return false; 820 } 821 Message msg = sm.obtainMessage(HeadsetClientStateMachine.EXPLICIT_CALL_TRANSFER); 822 sm.sendMessage(msg); 823 return true; 824 } 825 826 public Bundle getCurrentAgEvents(BluetoothDevice device) { 827 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 828 HeadsetClientStateMachine sm = getStateMachine(device); 829 if (sm == null) { 830 Log.e(TAG, "Cannot allocate SM for device " + device); 831 return null; 832 } 833 834 int connectionState = sm.getConnectionState(device); 835 if (connectionState != BluetoothProfile.STATE_CONNECTED) { 836 return null; 837 } 838 return sm.getCurrentAgEvents(); 839 } 840 841 public Bundle getCurrentAgFeatures(BluetoothDevice device) { 842 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 843 HeadsetClientStateMachine sm = getStateMachine(device); 844 if (sm == null) { 845 Log.e(TAG, "Cannot allocate SM for device " + device); 846 return null; 847 } 848 int connectionState = sm.getConnectionState(device); 849 if (connectionState != BluetoothProfile.STATE_CONNECTED) { 850 return null; 851 } 852 return sm.getCurrentAgFeatures(); 853 } 854 855 // Handle messages from native (JNI) to java 856 public void messageFromNative(StackEvent stackEvent) { 857 HeadsetClientStateMachine sm = getStateMachine(stackEvent.device); 858 if (sm == null) { 859 Log.w(TAG, "No SM found for event " + stackEvent); 860 } 861 862 sm.sendMessage(StackEvent.STACK_EVENT, stackEvent); 863 } 864 865 // State machine management 866 private synchronized HeadsetClientStateMachine getStateMachine(BluetoothDevice device) { 867 if (device == null) { 868 Log.e(TAG, "getStateMachine failed: Device cannot be null"); 869 return null; 870 } 871 872 HeadsetClientStateMachine sm = mStateMachineMap.get(device); 873 if (sm != null) { 874 if (DBG) { 875 Log.d(TAG, "Found SM for device " + device); 876 } 877 return sm; 878 } 879 880 // There is a possibility of a DOS attack if someone populates here with a lot of fake 881 // BluetoothAddresses. If it so happens instead of blowing up we can atleast put a limit on 882 // how long the attack would survive 883 if (mStateMachineMap.keySet().size() > MAX_STATE_MACHINES_POSSIBLE) { 884 Log.e(TAG, "Max state machines reached, possible DOS attack " 885 + MAX_STATE_MACHINES_POSSIBLE); 886 return null; 887 } 888 889 // Allocate a new SM 890 Log.d(TAG, "Creating a new state machine"); 891 sm = mSmFactory.make(this, mSmThread); 892 mStateMachineMap.put(device, sm); 893 return sm; 894 } 895 896 // Check if any of the state machines have routed the SCO audio stream. 897 synchronized boolean isScoRouted() { 898 for (Map.Entry<BluetoothDevice, HeadsetClientStateMachine> entry : mStateMachineMap 899 .entrySet()) { 900 if (entry.getValue() != null) { 901 int audioState = entry.getValue().getAudioState(entry.getKey()); 902 if (audioState == BluetoothHeadsetClient.STATE_AUDIO_CONNECTED) { 903 if (DBG) { 904 Log.d(TAG, "Device " + entry.getKey() + " audio state " + audioState 905 + " Connected"); 906 } 907 return true; 908 } 909 } 910 } 911 return false; 912 } 913 914 @Override 915 public synchronized void dump(StringBuilder sb) { 916 super.dump(sb); 917 for (HeadsetClientStateMachine sm : mStateMachineMap.values()) { 918 if (sm != null) { 919 println(sb, "State machine:"); 920 println(sb, "============="); 921 sm.dump(sb); 922 } 923 } 924 } 925 926 // For testing 927 protected synchronized Map<BluetoothDevice, HeadsetClientStateMachine> getStateMachineMap() { 928 return mStateMachineMap; 929 } 930 931 protected void setSMFactory(HeadsetClientStateMachineFactory factory) { 932 mSmFactory = factory; 933 } 934} 935