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