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