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