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