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