1 /* 2 * Copyright (C) 2006 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.phone; 18 19 import android.app.Service; 20 import android.bluetooth.BluetoothAdapter; 21 import android.bluetooth.BluetoothAudioGateway; 22 import android.bluetooth.BluetoothAudioGateway.IncomingConnectionInfo; 23 import android.bluetooth.BluetoothDevice; 24 import android.bluetooth.BluetoothHeadset; 25 import android.bluetooth.BluetoothProfile; 26 import android.bluetooth.BluetoothUuid; 27 import android.bluetooth.HeadsetBase; 28 import android.bluetooth.IBluetooth; 29 import android.bluetooth.IBluetoothHeadset; 30 import android.content.BroadcastReceiver; 31 import android.content.Context; 32 import android.content.Intent; 33 import android.content.IntentFilter; 34 import android.media.AudioManager; 35 import android.os.Handler; 36 import android.os.IBinder; 37 import android.os.Message; 38 import android.os.ParcelUuid; 39 import android.os.PowerManager; 40 import android.os.RemoteException; 41 import android.os.ServiceManager; 42 import android.provider.Settings; 43 import android.util.Log; 44 45 import java.util.ArrayList; 46 import java.util.List; 47 import java.util.concurrent.ConcurrentHashMap; 48 49 /** 50 * Provides Bluetooth Headset and Handsfree profile, as a service in 51 * the Phone application. 52 * @hide 53 */ 54 public class BluetoothHeadsetService extends Service { 55 private static final String TAG = "Bluetooth HSHFP"; 56 private static final boolean DBG = true; 57 58 private static final String PREF_NAME = BluetoothHeadsetService.class.getSimpleName(); 59 private static final String PREF_LAST_HEADSET = "lastHeadsetAddress"; 60 61 private static final int PHONE_STATE_CHANGED = 1; 62 63 private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN; 64 private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH; 65 66 private static boolean sHasStarted = false; 67 68 private BluetoothDevice mDeviceSdpQuery; 69 private BluetoothAdapter mAdapter; 70 private IBluetooth mBluetoothService; 71 private PowerManager mPowerManager; 72 private BluetoothAudioGateway mAg; 73 private BluetoothHandsfree mBtHandsfree; 74 private ConcurrentHashMap<BluetoothDevice, BluetoothRemoteHeadset> mRemoteHeadsets; 75 private BluetoothDevice mAudioConnectedDevice; 76 77 @Override 78 public void onCreate() { 79 super.onCreate(); 80 mAdapter = BluetoothAdapter.getDefaultAdapter(); 81 mPowerManager = (PowerManager) getSystemService(Context.POWER_SERVICE); 82 mBtHandsfree = PhoneApp.getInstance().getBluetoothHandsfree(); 83 mAg = new BluetoothAudioGateway(mAdapter); 84 IntentFilter filter = new IntentFilter( 85 BluetoothDevice.ACTION_ACL_DISCONNECT_REQUESTED); 86 filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); 87 filter.addAction(AudioManager.VOLUME_CHANGED_ACTION); 88 filter.addAction(BluetoothDevice.ACTION_UUID); 89 registerReceiver(mBluetoothReceiver, filter); 90 91 IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_SERVICE); 92 if (b == null) { 93 throw new RuntimeException("Bluetooth service not available"); 94 } 95 mBluetoothService = IBluetooth.Stub.asInterface(b); 96 mRemoteHeadsets = new ConcurrentHashMap<BluetoothDevice, BluetoothRemoteHeadset>(); 97 } 98 99 private class BluetoothRemoteHeadset { 100 private int mState; 101 private int mAudioState; 102 private int mHeadsetType; 103 private HeadsetBase mHeadset; 104 private IncomingConnectionInfo mIncomingInfo; 105 106 BluetoothRemoteHeadset() { 107 mState = BluetoothProfile.STATE_DISCONNECTED; 108 mHeadsetType = BluetoothHandsfree.TYPE_UNKNOWN; 109 mHeadset = null; 110 mIncomingInfo = null; 111 mAudioState = BluetoothHeadset.STATE_AUDIO_DISCONNECTED; 112 } 113 114 BluetoothRemoteHeadset(int headsetType, IncomingConnectionInfo incomingInfo) { 115 mState = BluetoothProfile.STATE_DISCONNECTED; 116 mHeadsetType = headsetType; 117 mHeadset = null; 118 mIncomingInfo = incomingInfo; 119 mAudioState = BluetoothHeadset.STATE_AUDIO_DISCONNECTED; 120 } 121 } 122 123 synchronized private BluetoothDevice getCurrentDevice() { 124 for (BluetoothDevice device : mRemoteHeadsets.keySet()) { 125 int state = mRemoteHeadsets.get(device).mState; 126 if (state == BluetoothProfile.STATE_CONNECTING || 127 state == BluetoothProfile.STATE_CONNECTED) { 128 return device; 129 } 130 } 131 return null; 132 } 133 134 @Override 135 public void onStart(Intent intent, int startId) { 136 if (mAdapter == null) { 137 Log.w(TAG, "Stopping BluetoothHeadsetService: device does not have BT"); 138 stopSelf(); 139 } else { 140 if (!sHasStarted) { 141 if (DBG) log("Starting BluetoothHeadsetService"); 142 if (mAdapter.isEnabled()) { 143 mAg.start(mIncomingConnectionHandler); 144 mBtHandsfree.onBluetoothEnabled(); 145 } 146 sHasStarted = true; 147 } 148 } 149 } 150 151 private final Handler mIncomingConnectionHandler = new Handler() { 152 @Override 153 public void handleMessage(Message msg) { 154 synchronized(BluetoothHeadsetService.this) { 155 IncomingConnectionInfo info = (IncomingConnectionInfo)msg.obj; 156 int type = BluetoothHandsfree.TYPE_UNKNOWN; 157 switch(msg.what) { 158 case BluetoothAudioGateway.MSG_INCOMING_HEADSET_CONNECTION: 159 type = BluetoothHandsfree.TYPE_HEADSET; 160 break; 161 case BluetoothAudioGateway.MSG_INCOMING_HANDSFREE_CONNECTION: 162 type = BluetoothHandsfree.TYPE_HANDSFREE; 163 break; 164 } 165 166 Log.i(TAG, "Incoming rfcomm (" + BluetoothHandsfree.typeToString(type) + 167 ") connection from " + info.mRemoteDevice + "on channel " + 168 info.mRfcommChan); 169 170 int priority = BluetoothProfile.PRIORITY_OFF; 171 HeadsetBase headset; 172 priority = getPriority(info.mRemoteDevice); 173 if (priority <= BluetoothProfile.PRIORITY_OFF) { 174 Log.i(TAG, "Rejecting incoming connection because priority = " + priority); 175 176 headset = new HeadsetBase(mPowerManager, mAdapter, 177 info.mRemoteDevice, 178 info.mSocketFd, info.mRfcommChan, 179 null); 180 headset.disconnect(); 181 try { 182 mBluetoothService.notifyIncomingConnection(info.mRemoteDevice.getAddress(), 183 true); 184 } catch (RemoteException e) { 185 Log.e(TAG, "notifyIncomingConnection", e); 186 } 187 return; 188 } 189 190 BluetoothRemoteHeadset remoteHeadset; 191 BluetoothDevice device = getCurrentDevice(); 192 193 int state = BluetoothProfile.STATE_DISCONNECTED; 194 if (device != null) { 195 state = mRemoteHeadsets.get(device).mState; 196 } 197 198 switch (state) { 199 case BluetoothProfile.STATE_DISCONNECTED: 200 // headset connecting us, lets join 201 remoteHeadset = new BluetoothRemoteHeadset(type, info); 202 mRemoteHeadsets.put(info.mRemoteDevice, remoteHeadset); 203 204 try { 205 mBluetoothService.notifyIncomingConnection( 206 info.mRemoteDevice.getAddress(), false); 207 } catch (RemoteException e) { 208 Log.e(TAG, "notifyIncomingConnection"); 209 } 210 break; 211 case BluetoothProfile.STATE_CONNECTING: 212 if (!info.mRemoteDevice.equals(device)) { 213 // different headset, ignoring 214 Log.i(TAG, "Already attempting connect to " + device + 215 ", disconnecting " + info.mRemoteDevice); 216 217 headset = new HeadsetBase(mPowerManager, mAdapter, 218 info.mRemoteDevice, 219 info.mSocketFd, info.mRfcommChan, 220 null); 221 headset.disconnect(); 222 break; 223 } 224 225 // Incoming and Outgoing connections to the same headset. 226 // The state machine manager will cancel outgoing and accept the incoming one. 227 // Update the state 228 mRemoteHeadsets.get(info.mRemoteDevice).mHeadsetType = type; 229 mRemoteHeadsets.get(info.mRemoteDevice).mIncomingInfo = info; 230 231 try { 232 mBluetoothService.notifyIncomingConnection( 233 info.mRemoteDevice.getAddress(), false); 234 } catch (RemoteException e) { 235 Log.e(TAG, "notifyIncomingConnection"); 236 } 237 break; 238 case BluetoothProfile.STATE_CONNECTED: 239 Log.i(TAG, "Already connected to " + device + ", disconnecting " + 240 info.mRemoteDevice); 241 rejectIncomingConnection(info); 242 break; 243 } 244 } 245 } 246 }; 247 248 private void rejectIncomingConnection(IncomingConnectionInfo info) { 249 HeadsetBase headset = new HeadsetBase(mPowerManager, mAdapter, 250 info.mRemoteDevice, info.mSocketFd, info.mRfcommChan, null); 251 headset.disconnect(); 252 } 253 254 255 private final BroadcastReceiver mBluetoothReceiver = new BroadcastReceiver() { 256 257 @Override 258 public void onReceive(Context context, Intent intent) { 259 String action = intent.getAction(); 260 BluetoothDevice device = 261 intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 262 263 BluetoothDevice currDevice = getCurrentDevice(); 264 int state = BluetoothProfile.STATE_DISCONNECTED; 265 if (currDevice != null) { 266 state = mRemoteHeadsets.get(currDevice).mState; 267 } 268 269 if ((state == BluetoothProfile.STATE_CONNECTED || 270 state == BluetoothProfile.STATE_CONNECTING) && 271 action.equals(BluetoothDevice.ACTION_ACL_DISCONNECT_REQUESTED) && 272 device.equals(currDevice)) { 273 try { 274 mBinder.disconnect(currDevice); 275 } catch (RemoteException e) {} 276 } else if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) { 277 switch (intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, 278 BluetoothAdapter.ERROR)) { 279 case BluetoothAdapter.STATE_ON: 280 mAg.start(mIncomingConnectionHandler); 281 mBtHandsfree.onBluetoothEnabled(); 282 break; 283 case BluetoothAdapter.STATE_TURNING_OFF: 284 mBtHandsfree.onBluetoothDisabled(); 285 mAg.stop(); 286 if (currDevice != null) { 287 try { 288 mBinder.disconnect(currDevice); 289 } catch (RemoteException e) {} 290 } 291 break; 292 } 293 } else if (action.equals(AudioManager.VOLUME_CHANGED_ACTION)) { 294 int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1); 295 if (streamType == AudioManager.STREAM_BLUETOOTH_SCO) { 296 mBtHandsfree.sendScoGainUpdate(intent.getIntExtra( 297 AudioManager.EXTRA_VOLUME_STREAM_VALUE, 0)); 298 } 299 300 } else if (action.equals(BluetoothDevice.ACTION_UUID)) { 301 if (device.equals(mDeviceSdpQuery) && device.equals(currDevice)) { 302 // We have got SDP records for the device we are interested in. 303 getSdpRecordsAndConnect(device); 304 } 305 } 306 } 307 }; 308 309 private static final int CONNECT_HEADSET_DELAYED = 1; 310 private Handler mHandler = new Handler() { 311 @Override 312 public void handleMessage(Message msg) { 313 switch (msg.what) { 314 case CONNECT_HEADSET_DELAYED: 315 BluetoothDevice device = (BluetoothDevice) msg.obj; 316 getSdpRecordsAndConnect(device); 317 break; 318 } 319 } 320 }; 321 322 @Override 323 public IBinder onBind(Intent intent) { 324 return mBinder; 325 } 326 327 // ------------------------------------------------------------------ 328 // Bluetooth Headset Connect 329 // ------------------------------------------------------------------ 330 private static final int RFCOMM_CONNECTED = 1; 331 private static final int RFCOMM_ERROR = 2; 332 333 private long mTimestamp; 334 335 /** 336 * Thread for RFCOMM connection 337 * Messages are sent to mConnectingStatusHandler as connection progresses. 338 */ 339 private RfcommConnectThread mConnectThread; 340 private class RfcommConnectThread extends Thread { 341 private BluetoothDevice device; 342 private int channel; 343 private int type; 344 345 private static final int EINTERRUPT = -1000; 346 private static final int ECONNREFUSED = -111; 347 348 public RfcommConnectThread(BluetoothDevice device, int channel, int type) { 349 super(); 350 this.device = device; 351 this.channel = channel; 352 this.type = type; 353 } 354 355 private int waitForConnect(HeadsetBase headset) { 356 // Try to connect for 20 seconds 357 int result = 0; 358 for (int i=0; i < 40 && result == 0; i++) { 359 // waitForAsyncConnect returns 0 on timeout, 1 on success, < 0 on error. 360 result = headset.waitForAsyncConnect(500, mConnectedStatusHandler); 361 if (isInterrupted()) { 362 headset.disconnect(); 363 return EINTERRUPT; 364 } 365 } 366 return result; 367 } 368 369 @Override 370 public void run() { 371 long timestamp; 372 373 timestamp = System.currentTimeMillis(); 374 HeadsetBase headset = new HeadsetBase(mPowerManager, mAdapter, 375 device, channel); 376 377 int result = waitForConnect(headset); 378 379 if (result != EINTERRUPT && result != 1) { 380 if (result == ECONNREFUSED && mDeviceSdpQuery == null) { 381 // The rfcomm channel number might have changed, do SDP 382 // query and try to connect again. 383 mDeviceSdpQuery = getCurrentDevice(); 384 device.fetchUuidsWithSdp(); 385 mConnectThread = null; 386 return; 387 } else { 388 Log.i(TAG, "Trying to connect to rfcomm socket again after 1 sec"); 389 try { 390 sleep(1000); // 1 second 391 } catch (InterruptedException e) { 392 return; 393 } 394 } 395 result = waitForConnect(headset); 396 } 397 mDeviceSdpQuery = null; 398 if (result == EINTERRUPT) return; 399 400 if (DBG) log("RFCOMM connection attempt took " + 401 (System.currentTimeMillis() - timestamp) + " ms"); 402 if (isInterrupted()) { 403 headset.disconnect(); 404 return; 405 } 406 if (result < 0) { 407 Log.w(TAG, "headset.waitForAsyncConnect() error: " + result); 408 mConnectingStatusHandler.obtainMessage(RFCOMM_ERROR).sendToTarget(); 409 return; 410 } else if (result == 0) { 411 mConnectingStatusHandler.obtainMessage(RFCOMM_ERROR).sendToTarget(); 412 Log.w(TAG, "mHeadset.waitForAsyncConnect() error: " + result + "(timeout)"); 413 return; 414 } else { 415 mConnectingStatusHandler.obtainMessage(RFCOMM_CONNECTED, headset).sendToTarget(); 416 } 417 } 418 } 419 420 /** 421 * Receives events from mConnectThread back in the main thread. 422 */ 423 private final Handler mConnectingStatusHandler = new Handler() { 424 @Override 425 public void handleMessage(Message msg) { 426 BluetoothDevice device = getCurrentDevice(); 427 if (device == null || 428 mRemoteHeadsets.get(device).mState != BluetoothProfile.STATE_CONNECTING) { 429 return; // stale events 430 } 431 432 switch (msg.what) { 433 case RFCOMM_ERROR: 434 if (DBG) log("Rfcomm error"); 435 mConnectThread = null; 436 setState(device, BluetoothProfile.STATE_DISCONNECTED); 437 break; 438 case RFCOMM_CONNECTED: 439 if (DBG) log("Rfcomm connected"); 440 mConnectThread = null; 441 HeadsetBase headset = (HeadsetBase)msg.obj; 442 setState(device, BluetoothProfile.STATE_CONNECTED); 443 444 mRemoteHeadsets.get(device).mHeadset = headset; 445 mBtHandsfree.connectHeadset(headset, mRemoteHeadsets.get(device).mHeadsetType); 446 break; 447 } 448 } 449 }; 450 451 /** 452 * Receives events from a connected RFCOMM socket back in the main thread. 453 */ 454 private final Handler mConnectedStatusHandler = new Handler() { 455 @Override 456 public void handleMessage(Message msg) { 457 switch (msg.what) { 458 case HeadsetBase.RFCOMM_DISCONNECTED: 459 mBtHandsfree.resetAtState(); 460 mBtHandsfree.setVirtualCallInProgress(false); 461 BluetoothDevice device = getCurrentDevice(); 462 if (device != null) { 463 setState(device, BluetoothProfile.STATE_DISCONNECTED); 464 } 465 break; 466 } 467 } 468 }; 469 470 private synchronized void setState(BluetoothDevice device, int state) { 471 int prevState = mRemoteHeadsets.get(device).mState; 472 if (state != prevState) { 473 if (DBG) log("Device: " + device + 474 " Headset state" + prevState + " -> " + state); 475 if (prevState == BluetoothProfile.STATE_CONNECTED) { 476 // Headset is disconnecting, stop the parser. 477 mBtHandsfree.disconnectHeadset(); 478 } 479 Intent intent = new Intent(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); 480 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState); 481 intent.putExtra(BluetoothProfile.EXTRA_STATE, state); 482 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 483 if (state == BluetoothProfile.STATE_DISCONNECTED) { 484 mRemoteHeadsets.get(device).mHeadset = null; 485 mRemoteHeadsets.get(device).mHeadsetType = BluetoothHandsfree.TYPE_UNKNOWN; 486 } 487 488 mRemoteHeadsets.get(device).mState = state; 489 490 sendBroadcast(intent, BLUETOOTH_PERM); 491 if (state == BluetoothHeadset.STATE_CONNECTED) { 492 // Set the priority to AUTO_CONNECT 493 setPriority(device, BluetoothHeadset.PRIORITY_AUTO_CONNECT); 494 adjustOtherHeadsetPriorities(device); 495 } 496 try { 497 mBluetoothService.sendConnectionStateChange(device, BluetoothProfile.HEADSET, 498 state, prevState); 499 } catch (RemoteException e) { 500 Log.e(TAG, "sendConnectionStateChange: exception"); 501 } 502 } 503 } 504 505 private void adjustOtherHeadsetPriorities(BluetoothDevice connectedDevice) { 506 for (BluetoothDevice device : mAdapter.getBondedDevices()) { 507 if (getPriority(device) >= BluetoothHeadset.PRIORITY_AUTO_CONNECT && 508 !device.equals(connectedDevice)) { 509 setPriority(device, BluetoothHeadset.PRIORITY_ON); 510 } 511 } 512 } 513 514 private void setPriority(BluetoothDevice device, int priority) { 515 try { 516 mBinder.setPriority(device, priority); 517 } catch (RemoteException e) { 518 Log.e(TAG, "Error while setting priority for: " + device); 519 } 520 } 521 522 private int getPriority(BluetoothDevice device) { 523 try { 524 return mBinder.getPriority(device); 525 } catch (RemoteException e) { 526 Log.e(TAG, "Error while getting priority for: " + device); 527 } 528 return BluetoothProfile.PRIORITY_UNDEFINED; 529 } 530 531 private synchronized void getSdpRecordsAndConnect(BluetoothDevice device) { 532 if (!device.equals(getCurrentDevice())) { 533 // stale 534 return; 535 } 536 537 // Check if incoming connection has already connected. 538 if (mRemoteHeadsets.get(device).mState == BluetoothProfile.STATE_CONNECTED) { 539 return; 540 } 541 542 ParcelUuid[] uuids = device.getUuids(); 543 ParcelUuid[] localUuids = mAdapter.getUuids(); 544 int type = BluetoothHandsfree.TYPE_UNKNOWN; 545 if (uuids != null) { 546 if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Handsfree) && 547 BluetoothUuid.isUuidPresent(localUuids, BluetoothUuid.Handsfree_AG)) { 548 log("SDP UUID: TYPE_HANDSFREE"); 549 type = BluetoothHandsfree.TYPE_HANDSFREE; 550 mRemoteHeadsets.get(device).mHeadsetType = type; 551 int channel = device.getServiceChannel(BluetoothUuid.Handsfree); 552 mConnectThread = new RfcommConnectThread(device, channel, type); 553 if (mAdapter.isDiscovering()) { 554 mAdapter.cancelDiscovery(); 555 } 556 mConnectThread.start(); 557 if (getPriority(device) < BluetoothHeadset.PRIORITY_AUTO_CONNECT) { 558 setPriority(device, BluetoothHeadset.PRIORITY_AUTO_CONNECT); 559 } 560 return; 561 } else if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.HSP) && 562 BluetoothUuid.isUuidPresent(localUuids, BluetoothUuid.HSP_AG)) { 563 log("SDP UUID: TYPE_HEADSET"); 564 type = BluetoothHandsfree.TYPE_HEADSET; 565 mRemoteHeadsets.get(device).mHeadsetType = type; 566 int channel = device.getServiceChannel(BluetoothUuid.HSP); 567 mConnectThread = new RfcommConnectThread(device, channel, type); 568 if (mAdapter.isDiscovering()) { 569 mAdapter.cancelDiscovery(); 570 } 571 mConnectThread.start(); 572 if (getPriority(device) < BluetoothHeadset.PRIORITY_AUTO_CONNECT) { 573 setPriority(device, BluetoothHeadset.PRIORITY_AUTO_CONNECT); 574 } 575 return; 576 } 577 } 578 log("SDP UUID: TYPE_UNKNOWN"); 579 mRemoteHeadsets.get(device).mHeadsetType = type; 580 setState(device, BluetoothProfile.STATE_DISCONNECTED); 581 return; 582 } 583 584 /** 585 * Handlers for incoming service calls 586 */ 587 private final IBluetoothHeadset.Stub mBinder = new IBluetoothHeadset.Stub() { 588 public int getConnectionState(BluetoothDevice device) { 589 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 590 BluetoothRemoteHeadset headset = mRemoteHeadsets.get(device); 591 if (headset == null) { 592 return BluetoothProfile.STATE_DISCONNECTED; 593 } 594 return headset.mState; 595 } 596 597 public List<BluetoothDevice> getConnectedDevices() { 598 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 599 return getDevicesMatchingConnectionStates( 600 new int[] {BluetoothProfile.STATE_CONNECTED}); 601 } 602 603 public boolean connect(BluetoothDevice device) { 604 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 605 "Need BLUETOOTH_ADMIN permission"); 606 synchronized (BluetoothHeadsetService.this) { 607 BluetoothDevice currDevice = getCurrentDevice(); 608 609 if (currDevice == device || 610 getPriority(device) == BluetoothProfile.PRIORITY_OFF) { 611 return false; 612 } 613 if (currDevice != null) { 614 disconnect(currDevice); 615 } 616 try { 617 return mBluetoothService.connectHeadset(device.getAddress()); 618 } catch (RemoteException e) { 619 Log.e(TAG, "connectHeadset"); 620 return false; 621 } 622 } 623 } 624 625 public boolean disconnect(BluetoothDevice device) { 626 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 627 "Need BLUETOOTH_ADMIN permission"); 628 synchronized (BluetoothHeadsetService.this) { 629 BluetoothRemoteHeadset headset = mRemoteHeadsets.get(device); 630 if (headset == null || 631 headset.mState == BluetoothProfile.STATE_DISCONNECTED || 632 headset.mState == BluetoothProfile.STATE_DISCONNECTING) { 633 return false; 634 } 635 try { 636 return mBluetoothService.disconnectHeadset(device.getAddress()); 637 } catch (RemoteException e) { 638 Log.e(TAG, "disconnectHeadset"); 639 return false; 640 } 641 } 642 } 643 644 public synchronized boolean isAudioConnected(BluetoothDevice device) { 645 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 646 if (device.equals(mAudioConnectedDevice)) return true; 647 return false; 648 } 649 650 public synchronized List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 651 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 652 List<BluetoothDevice> headsets = new ArrayList<BluetoothDevice>(); 653 for (BluetoothDevice device: mRemoteHeadsets.keySet()) { 654 int headsetState = getConnectionState(device); 655 for (int state : states) { 656 if (state == headsetState) { 657 headsets.add(device); 658 break; 659 } 660 } 661 } 662 return headsets; 663 } 664 665 public boolean startVoiceRecognition(BluetoothDevice device) { 666 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 667 synchronized (BluetoothHeadsetService.this) { 668 if (device == null || 669 mRemoteHeadsets.get(device) == null || 670 mRemoteHeadsets.get(device).mState != BluetoothProfile.STATE_CONNECTED) { 671 return false; 672 } 673 return mBtHandsfree.startVoiceRecognition(); 674 } 675 } 676 677 public boolean stopVoiceRecognition(BluetoothDevice device) { 678 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 679 synchronized (BluetoothHeadsetService.this) { 680 if (device == null || 681 mRemoteHeadsets.get(device) == null || 682 mRemoteHeadsets.get(device).mState != BluetoothProfile.STATE_CONNECTED) { 683 return false; 684 } 685 686 return mBtHandsfree.stopVoiceRecognition(); 687 } 688 } 689 690 public int getBatteryUsageHint(BluetoothDevice device) { 691 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 692 693 return HeadsetBase.getAtInputCount(); 694 } 695 696 public int getPriority(BluetoothDevice device) { 697 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 698 "Need BLUETOOTH_ADMIN permission"); 699 synchronized (BluetoothHeadsetService.this) { 700 int priority = Settings.Secure.getInt(getContentResolver(), 701 Settings.Secure.getBluetoothHeadsetPriorityKey(device.getAddress()), 702 BluetoothProfile.PRIORITY_UNDEFINED); 703 return priority; 704 } 705 } 706 707 public boolean setPriority(BluetoothDevice device, int priority) { 708 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 709 "Need BLUETOOTH_ADMIN permission"); 710 synchronized (BluetoothHeadsetService.this) { 711 Settings.Secure.putInt(getContentResolver(), 712 Settings.Secure.getBluetoothHeadsetPriorityKey(device.getAddress()), 713 priority); 714 if (DBG) log("Saved priority " + device + " = " + priority); 715 return true; 716 } 717 } 718 719 public boolean createIncomingConnect(BluetoothDevice device) { 720 synchronized (BluetoothHeadsetService.this) { 721 HeadsetBase headset; 722 setState(device, BluetoothProfile.STATE_CONNECTING); 723 724 IncomingConnectionInfo info = mRemoteHeadsets.get(device).mIncomingInfo; 725 headset = new HeadsetBase(mPowerManager, mAdapter, 726 device, 727 info.mSocketFd, info.mRfcommChan, 728 mConnectedStatusHandler); 729 730 mRemoteHeadsets.get(device).mHeadset = headset; 731 732 mConnectingStatusHandler.obtainMessage(RFCOMM_CONNECTED, headset).sendToTarget(); 733 return true; 734 } 735 } 736 737 public boolean startScoUsingVirtualVoiceCall(BluetoothDevice device) { 738 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 739 synchronized (BluetoothHeadsetService.this) { 740 if (device == null || 741 mRemoteHeadsets.get(device) == null || 742 mRemoteHeadsets.get(device).mState != BluetoothProfile.STATE_CONNECTED || 743 getAudioState(device) != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { 744 return false; 745 } 746 return mBtHandsfree.initiateScoUsingVirtualVoiceCall(); 747 } 748 } 749 750 public boolean stopScoUsingVirtualVoiceCall(BluetoothDevice device) { 751 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 752 synchronized (BluetoothHeadsetService.this) { 753 if (device == null || 754 mRemoteHeadsets.get(device) == null || 755 mRemoteHeadsets.get(device).mState != BluetoothProfile.STATE_CONNECTED || 756 getAudioState(device) == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { 757 return false; 758 } 759 return mBtHandsfree.terminateScoUsingVirtualVoiceCall(); 760 } 761 } 762 763 public boolean rejectIncomingConnect(BluetoothDevice device) { 764 synchronized (BluetoothHeadsetService.this) { 765 BluetoothRemoteHeadset headset = mRemoteHeadsets.get(device); 766 if (headset != null) { 767 IncomingConnectionInfo info = headset.mIncomingInfo; 768 rejectIncomingConnection(info); 769 } else { 770 Log.e(TAG, "Error no record of remote headset"); 771 } 772 return true; 773 } 774 } 775 776 public boolean acceptIncomingConnect(BluetoothDevice device) { 777 synchronized (BluetoothHeadsetService.this) { 778 HeadsetBase headset; 779 BluetoothRemoteHeadset cachedHeadset = mRemoteHeadsets.get(device); 780 if (cachedHeadset == null) { 781 Log.e(TAG, "Cached Headset is Null in acceptIncomingConnect"); 782 return false; 783 } 784 IncomingConnectionInfo info = cachedHeadset.mIncomingInfo; 785 headset = new HeadsetBase(mPowerManager, mAdapter, 786 device, 787 info.mSocketFd, info.mRfcommChan, 788 mConnectedStatusHandler); 789 790 setState(device, BluetoothProfile.STATE_CONNECTED); 791 792 cachedHeadset.mHeadset = headset; 793 mBtHandsfree.connectHeadset(headset, cachedHeadset.mHeadsetType); 794 795 if (DBG) log("Successfully used incoming connection"); 796 return true; 797 } 798 } 799 800 public boolean cancelConnectThread() { 801 synchronized (BluetoothHeadsetService.this) { 802 if (mConnectThread != null) { 803 // cancel the connection thread 804 mConnectThread.interrupt(); 805 try { 806 mConnectThread.join(); 807 } catch (InterruptedException e) { 808 Log.e(TAG, "Connection cancelled twice?", e); 809 } 810 mConnectThread = null; 811 } 812 return true; 813 } 814 } 815 816 public boolean connectHeadsetInternal(BluetoothDevice device) { 817 synchronized (BluetoothHeadsetService.this) { 818 BluetoothDevice currDevice = getCurrentDevice(); 819 if (currDevice == null) { 820 BluetoothRemoteHeadset headset = new BluetoothRemoteHeadset(); 821 mRemoteHeadsets.put(device, headset); 822 823 setState(device, BluetoothProfile.STATE_CONNECTING); 824 if (device.getUuids() == null) { 825 // We might not have got the UUID change notification from 826 // Bluez yet, if we have just paired. Try after 1.5 secs. 827 Message msg = new Message(); 828 msg.what = CONNECT_HEADSET_DELAYED; 829 msg.obj = device; 830 mHandler.sendMessageDelayed(msg, 1500); 831 } else { 832 getSdpRecordsAndConnect(device); 833 } 834 return true; 835 } else { 836 Log.w(TAG, "connectHeadset(" + device + "): failed: already in state " + 837 mRemoteHeadsets.get(currDevice).mState + 838 " with headset " + currDevice); 839 } 840 return false; 841 } 842 } 843 844 public boolean disconnectHeadsetInternal(BluetoothDevice device) { 845 synchronized (BluetoothHeadsetService.this) { 846 BluetoothRemoteHeadset remoteHeadset = mRemoteHeadsets.get(device); 847 if (remoteHeadset == null) return false; 848 849 if (remoteHeadset.mState == BluetoothProfile.STATE_CONNECTED) { 850 // Send a dummy battery level message to force headset 851 // out of sniff mode so that it will immediately notice 852 // the disconnection. We are currently sending it for 853 // handsfree only. 854 // TODO: Call hci_conn_enter_active_mode() from 855 // rfcomm_send_disc() in the kernel instead. 856 // See http://b/1716887 857 setState(device, BluetoothProfile.STATE_DISCONNECTING); 858 859 HeadsetBase headset = remoteHeadset.mHeadset; 860 if (remoteHeadset.mHeadsetType == BluetoothHandsfree.TYPE_HANDSFREE) { 861 headset.sendURC("+CIEV: 7,3"); 862 } 863 864 if (headset != null) { 865 headset.disconnect(); 866 headset = null; 867 } 868 setState(device, BluetoothProfile.STATE_DISCONNECTED); 869 return true; 870 } else if (remoteHeadset.mState == BluetoothProfile.STATE_CONNECTING) { 871 // The state machine would have canceled the connect thread. 872 // Just set the state here. 873 setState(device, BluetoothProfile.STATE_DISCONNECTED); 874 return true; 875 } 876 return false; 877 } 878 } 879 880 public boolean setAudioState(BluetoothDevice device, int state) { 881 // mRemoteHeadsets handles put/get concurrency by itself 882 int prevState = mRemoteHeadsets.get(device).mAudioState; 883 mRemoteHeadsets.get(device).mAudioState = state; 884 if (state == BluetoothHeadset.STATE_AUDIO_CONNECTED) { 885 mAudioConnectedDevice = device; 886 } else if (state == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { 887 mAudioConnectedDevice = null; 888 } 889 Intent intent = new Intent(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED); 890 intent.putExtra(BluetoothHeadset.EXTRA_STATE, state); 891 intent.putExtra(BluetoothHeadset.EXTRA_PREVIOUS_STATE, prevState); 892 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 893 sendBroadcast(intent, android.Manifest.permission.BLUETOOTH); 894 if (DBG) log("AudioStateIntent: " + device + " State: " + state 895 + " PrevState: " + prevState); 896 return true; 897 } 898 899 public int getAudioState(BluetoothDevice device) { 900 // mRemoteHeadsets handles put/get concurrency by itself 901 BluetoothRemoteHeadset headset = mRemoteHeadsets.get(device); 902 if (headset == null) return BluetoothHeadset.STATE_AUDIO_DISCONNECTED; 903 904 return headset.mAudioState; 905 } 906 }; 907 908 @Override 909 public void onDestroy() { 910 super.onDestroy(); 911 if (DBG) log("Stopping BluetoothHeadsetService"); 912 unregisterReceiver(mBluetoothReceiver); 913 mBtHandsfree.onBluetoothDisabled(); 914 mAg.stop(); 915 sHasStarted = false; 916 BluetoothDevice device = getCurrentDevice(); 917 if (device != null) { 918 setState(device, BluetoothProfile.STATE_DISCONNECTED); 919 } 920 } 921 922 923 924 private static void log(String msg) { 925 Log.d(TAG, msg); 926 } 927 } 928