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