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