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 onCreate()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 BluetoothRemoteHeadset()106 BluetoothRemoteHeadset() { 107 mState = BluetoothHeadset.STATE_DISCONNECTED; 108 mHeadsetType = BluetoothHandsfree.TYPE_UNKNOWN; 109 mHeadset = null; 110 mIncomingInfo = null; 111 } 112 BluetoothRemoteHeadset(int headsetType, IncomingConnectionInfo incomingInfo)113 BluetoothRemoteHeadset(int headsetType, IncomingConnectionInfo incomingInfo) { 114 mState = BluetoothHeadset.STATE_DISCONNECTED; 115 mHeadsetType = headsetType; 116 mHeadset = null; 117 mIncomingInfo = incomingInfo; 118 } 119 } 120 getCurrentDevice()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 onStart(Intent intent, int startId)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 rejectIncomingConnection(info); 230 break; 231 } 232 } 233 } 234 }; 235 rejectIncomingConnection(IncomingConnectionInfo info)236 private void rejectIncomingConnection(IncomingConnectionInfo info) { 237 HeadsetBase headset = new HeadsetBase(mPowerManager, mAdapter, 238 info.mRemoteDevice, info.mSocketFd, info.mRfcommChan, null); 239 headset.disconnect(); 240 } 241 242 243 private final BroadcastReceiver mBluetoothReceiver = new BroadcastReceiver() { 244 245 @Override 246 public void onReceive(Context context, Intent intent) { 247 String action = intent.getAction(); 248 BluetoothDevice device = 249 intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 250 251 BluetoothDevice currDevice = getCurrentDevice(); 252 int state = BluetoothHeadset.STATE_DISCONNECTED; 253 if (currDevice != null) { 254 state = mRemoteHeadsets.get(currDevice).mState; 255 } 256 257 if ((state == BluetoothHeadset.STATE_CONNECTED || 258 state == BluetoothHeadset.STATE_CONNECTING) && 259 action.equals(BluetoothDevice.ACTION_ACL_DISCONNECT_REQUESTED) && 260 device.equals(currDevice)) { 261 try { 262 mBinder.disconnectHeadset(currDevice); 263 } catch (RemoteException e) {} 264 } else if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) { 265 switch (intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, 266 BluetoothAdapter.ERROR)) { 267 case BluetoothAdapter.STATE_ON: 268 adjustPriorities(); 269 mAg.start(mIncomingConnectionHandler); 270 mBtHandsfree.onBluetoothEnabled(); 271 break; 272 case BluetoothAdapter.STATE_TURNING_OFF: 273 mBtHandsfree.onBluetoothDisabled(); 274 mAg.stop(); 275 if (currDevice != null) { 276 setState(currDevice, BluetoothHeadset.STATE_DISCONNECTED, 277 BluetoothHeadset.RESULT_FAILURE, 278 BluetoothHeadset.LOCAL_DISCONNECT); 279 } 280 break; 281 } 282 } else if (action.equals(BluetoothDevice.ACTION_BOND_STATE_CHANGED)) { 283 int bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, 284 BluetoothDevice.ERROR); 285 switch(bondState) { 286 case BluetoothDevice.BOND_BONDED: 287 if (getPriority(device) == BluetoothHeadset.PRIORITY_UNDEFINED) { 288 setPriority(device, BluetoothHeadset.PRIORITY_ON); 289 } 290 break; 291 case BluetoothDevice.BOND_NONE: 292 setPriority(device, BluetoothHeadset.PRIORITY_UNDEFINED); 293 break; 294 } 295 } else if (action.equals(AudioManager.VOLUME_CHANGED_ACTION)) { 296 int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1); 297 if (streamType == AudioManager.STREAM_BLUETOOTH_SCO) { 298 mBtHandsfree.sendScoGainUpdate(intent.getIntExtra( 299 AudioManager.EXTRA_VOLUME_STREAM_VALUE, 0)); 300 } 301 302 } else if (action.equals(BluetoothDevice.ACTION_UUID)) { 303 if (device.equals(mDeviceSdpQuery) && device.equals(currDevice)) { 304 // We have got SDP records for the device we are interested in. 305 getSdpRecordsAndConnect(device); 306 } 307 } 308 } 309 }; 310 311 private static final int CONNECT_HEADSET_DELAYED = 1; 312 private Handler mHandler = new Handler() { 313 @Override 314 public void handleMessage(Message msg) { 315 switch (msg.what) { 316 case CONNECT_HEADSET_DELAYED: 317 BluetoothDevice device = (BluetoothDevice) msg.obj; 318 getSdpRecordsAndConnect(device); 319 break; 320 } 321 } 322 }; 323 324 @Override onBind(Intent intent)325 public IBinder onBind(Intent intent) { 326 return mBinder; 327 } 328 329 // ------------------------------------------------------------------ 330 // Bluetooth Headset Connect 331 // ------------------------------------------------------------------ 332 private static final int RFCOMM_CONNECTED = 1; 333 private static final int RFCOMM_ERROR = 2; 334 335 private long mTimestamp; 336 337 /** 338 * Thread for RFCOMM connection 339 * Messages are sent to mConnectingStatusHandler as connection progresses. 340 */ 341 private RfcommConnectThread mConnectThread; 342 private class RfcommConnectThread extends Thread { 343 private BluetoothDevice device; 344 private int channel; 345 private int type; 346 347 private static final int EINTERRUPT = -1000; 348 private static final int ECONNREFUSED = -111; 349 RfcommConnectThread(BluetoothDevice device, int channel, int type)350 public RfcommConnectThread(BluetoothDevice device, int channel, int type) { 351 super(); 352 this.device = device; 353 this.channel = channel; 354 this.type = type; 355 } 356 waitForConnect(HeadsetBase headset)357 private int waitForConnect(HeadsetBase headset) { 358 // Try to connect for 20 seconds 359 int result = 0; 360 for (int i=0; i < 40 && result == 0; i++) { 361 // waitForAsyncConnect returns 0 on timeout, 1 on success, < 0 on error. 362 result = headset.waitForAsyncConnect(500, mConnectedStatusHandler); 363 if (isInterrupted()) { 364 headset.disconnect(); 365 return EINTERRUPT; 366 } 367 } 368 return result; 369 } 370 371 @Override run()372 public void run() { 373 long timestamp; 374 375 timestamp = System.currentTimeMillis(); 376 HeadsetBase headset = new HeadsetBase(mPowerManager, mAdapter, device, channel); 377 378 int result = waitForConnect(headset); 379 380 if (result != EINTERRUPT && result != 1) { 381 if (result == ECONNREFUSED && mDeviceSdpQuery == null) { 382 // The rfcomm channel number might have changed, do SDP 383 // query and try to connect again. 384 mDeviceSdpQuery = getCurrentDevice(); 385 device.fetchUuidsWithSdp(); 386 mConnectThread = null; 387 return; 388 } else { 389 Log.i(TAG, "Trying to connect to rfcomm socket again after 1 sec"); 390 try { 391 sleep(1000); // 1 second 392 } catch (InterruptedException e) {} 393 } 394 result = waitForConnect(headset); 395 } 396 mDeviceSdpQuery = null; 397 if (result == EINTERRUPT) return; 398 399 if (DBG) log("RFCOMM connection attempt took " + 400 (System.currentTimeMillis() - timestamp) + " ms"); 401 if (isInterrupted()) { 402 headset.disconnect(); 403 return; 404 } 405 if (result < 0) { 406 Log.w(TAG, "headset.waitForAsyncConnect() error: " + result); 407 mConnectingStatusHandler.obtainMessage(RFCOMM_ERROR).sendToTarget(); 408 return; 409 } else if (result == 0) { 410 mConnectingStatusHandler.obtainMessage(RFCOMM_ERROR).sendToTarget(); 411 Log.w(TAG, "mHeadset.waitForAsyncConnect() error: " + result + "(timeout)"); 412 return; 413 } else { 414 mConnectingStatusHandler.obtainMessage(RFCOMM_CONNECTED, headset).sendToTarget(); 415 } 416 } 417 } 418 419 /** 420 * Receives events from mConnectThread back in the main thread. 421 */ 422 private final Handler mConnectingStatusHandler = new Handler() { 423 @Override 424 public void handleMessage(Message msg) { 425 BluetoothDevice device = getCurrentDevice(); 426 if (device == null || 427 mRemoteHeadsets.get(device).mState != BluetoothHeadset.STATE_CONNECTING) { 428 return; // stale events 429 } 430 431 switch (msg.what) { 432 case RFCOMM_ERROR: 433 if (DBG) log("Rfcomm error"); 434 mConnectThread = null; 435 setState(device, 436 BluetoothHeadset.STATE_DISCONNECTED, BluetoothHeadset.RESULT_FAILURE, 437 BluetoothHeadset.LOCAL_DISCONNECT); 438 break; 439 case RFCOMM_CONNECTED: 440 if (DBG) log("Rfcomm connected"); 441 mConnectThread = null; 442 HeadsetBase headset = (HeadsetBase)msg.obj; 443 setState(device, 444 BluetoothHeadset.STATE_CONNECTED, BluetoothHeadset.RESULT_SUCCESS); 445 446 mRemoteHeadsets.get(device).mHeadset = headset; 447 mBtHandsfree.connectHeadset(headset, mRemoteHeadsets.get(device).mHeadsetType); 448 break; 449 } 450 } 451 }; 452 453 /** 454 * Receives events from a connected RFCOMM socket back in the main thread. 455 */ 456 private final Handler mConnectedStatusHandler = new Handler() { 457 @Override 458 public void handleMessage(Message msg) { 459 switch (msg.what) { 460 case HeadsetBase.RFCOMM_DISCONNECTED: 461 mBtHandsfree.resetAtState(); 462 BluetoothDevice device = getCurrentDevice(); 463 if (device != null) { 464 setState(device, 465 BluetoothHeadset.STATE_DISCONNECTED, BluetoothHeadset.RESULT_FAILURE, 466 BluetoothHeadset.REMOTE_DISCONNECT); 467 } 468 break; 469 } 470 } 471 }; 472 setState(BluetoothDevice device, int state)473 private void setState(BluetoothDevice device, int state) { 474 setState(device, state, BluetoothHeadset.RESULT_SUCCESS); 475 } 476 setState(BluetoothDevice device, int state, int result)477 private void setState(BluetoothDevice device, int state, int result) { 478 setState(device, state, result, -1); 479 } 480 setState(BluetoothDevice device, int state, int result, int initiator)481 private synchronized void setState(BluetoothDevice device, 482 int state, int result, int initiator) { 483 int prevState = mRemoteHeadsets.get(device).mState; 484 if (state != prevState) { 485 if (DBG) log("Device: " + device + 486 " Headset state" + prevState + " -> " + state + ", result = " + result); 487 if (prevState == BluetoothHeadset.STATE_CONNECTED) { 488 mBtHandsfree.disconnectHeadset(); 489 } 490 Intent intent = new Intent(BluetoothHeadset.ACTION_STATE_CHANGED); 491 intent.putExtra(BluetoothHeadset.EXTRA_PREVIOUS_STATE, prevState); 492 intent.putExtra(BluetoothHeadset.EXTRA_STATE, state); 493 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 494 // Add Extra EXTRA_DISCONNECT_INITIATOR for DISCONNECTED state 495 if (state == BluetoothHeadset.STATE_DISCONNECTED) { 496 if (initiator == -1) { 497 log("Headset Disconnected Intent without Disconnect Initiator extra"); 498 } else { 499 intent.putExtra(BluetoothHeadset.EXTRA_DISCONNECT_INITIATOR, 500 initiator); 501 } 502 mRemoteHeadsets.get(device).mHeadset = null; 503 mRemoteHeadsets.get(device).mHeadsetType = BluetoothHandsfree.TYPE_UNKNOWN; 504 } 505 506 mRemoteHeadsets.get(device).mState = state; 507 508 sendBroadcast(intent, BLUETOOTH_PERM); 509 if (state == BluetoothHeadset.STATE_CONNECTED) { 510 // Set the priority to AUTO_CONNECT 511 setPriority(device, BluetoothHeadset.PRIORITY_AUTO_CONNECT); 512 adjustOtherHeadsetPriorities(device); 513 } 514 } 515 } 516 adjustOtherHeadsetPriorities(BluetoothDevice connectedDevice)517 private void adjustOtherHeadsetPriorities(BluetoothDevice connectedDevice) { 518 for (BluetoothDevice device : mAdapter.getBondedDevices()) { 519 if (getPriority(device) >= BluetoothHeadset.PRIORITY_AUTO_CONNECT && 520 !device.equals(connectedDevice)) { 521 setPriority(device, BluetoothHeadset.PRIORITY_ON); 522 } 523 } 524 } 525 setPriority(BluetoothDevice device, int priority)526 private void setPriority(BluetoothDevice device, int priority) { 527 try { 528 mBinder.setPriority(device, priority); 529 } catch (RemoteException e) { 530 Log.e(TAG, "Error while setting priority for: " + device); 531 } 532 } 533 getPriority(BluetoothDevice device)534 private int getPriority(BluetoothDevice device) { 535 try { 536 return mBinder.getPriority(device); 537 } catch (RemoteException e) { 538 Log.e(TAG, "Error while getting priority for: " + device); 539 } 540 return BluetoothHeadset.PRIORITY_UNDEFINED; 541 } 542 adjustPriorities()543 private void adjustPriorities() { 544 // This is to ensure backward compatibility. 545 // Only 1 device is set to AUTO_CONNECT 546 BluetoothDevice savedDevice = null; 547 int max_priority = BluetoothHeadset.PRIORITY_AUTO_CONNECT; 548 if (mAdapter.getBondedDevices() != null) { 549 for (BluetoothDevice device : mAdapter.getBondedDevices()) { 550 int priority = getPriority(device); 551 if (priority >= BluetoothHeadset.PRIORITY_AUTO_CONNECT) { 552 setPriority(device, BluetoothHeadset.PRIORITY_ON); 553 } 554 if (priority >= max_priority) { 555 max_priority = priority; 556 savedDevice = device; 557 } 558 } 559 if (savedDevice != null) { 560 setPriority(savedDevice, BluetoothHeadset.PRIORITY_AUTO_CONNECT); 561 } 562 } 563 } 564 getSdpRecordsAndConnect(BluetoothDevice device)565 private synchronized void getSdpRecordsAndConnect(BluetoothDevice device) { 566 if (!device.equals(getCurrentDevice())) { 567 // stale 568 return; 569 } 570 571 // Check if incoming connection has already connected. 572 if (mRemoteHeadsets.get(device).mState == BluetoothHeadset.STATE_CONNECTED) { 573 return; 574 } 575 576 ParcelUuid[] uuids = device.getUuids(); 577 int type = BluetoothHandsfree.TYPE_UNKNOWN; 578 if (uuids != null) { 579 if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Handsfree)) { 580 log("SDP UUID: TYPE_HANDSFREE"); 581 type = BluetoothHandsfree.TYPE_HANDSFREE; 582 mRemoteHeadsets.get(device).mHeadsetType = type; 583 int channel = device.getServiceChannel(BluetoothUuid.Handsfree); 584 mConnectThread = new RfcommConnectThread(device, channel, type); 585 mConnectThread.start(); 586 if (getPriority(device) < BluetoothHeadset.PRIORITY_AUTO_CONNECT) { 587 setPriority(device, BluetoothHeadset.PRIORITY_AUTO_CONNECT); 588 } 589 return; 590 } else if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.HSP)) { 591 log("SDP UUID: TYPE_HEADSET"); 592 type = BluetoothHandsfree.TYPE_HEADSET; 593 mRemoteHeadsets.get(device).mHeadsetType = type; 594 int channel = device.getServiceChannel(BluetoothUuid.HSP); 595 mConnectThread = new RfcommConnectThread(device, channel, type); 596 mConnectThread.start(); 597 if (getPriority(device) < BluetoothHeadset.PRIORITY_AUTO_CONNECT) { 598 setPriority(device, BluetoothHeadset.PRIORITY_AUTO_CONNECT); 599 } 600 return; 601 } 602 } 603 log("SDP UUID: TYPE_UNKNOWN"); 604 mRemoteHeadsets.get(device).mHeadsetType = type; 605 setState(device, BluetoothHeadset.STATE_DISCONNECTED, 606 BluetoothHeadset.RESULT_FAILURE, BluetoothHeadset.LOCAL_DISCONNECT); 607 return; 608 } 609 610 /** 611 * Handlers for incoming service calls 612 */ 613 private final IBluetoothHeadset.Stub mBinder = new IBluetoothHeadset.Stub() { 614 public int getState(BluetoothDevice device) { 615 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 616 BluetoothRemoteHeadset headset = mRemoteHeadsets.get(device); 617 if (headset == null) { 618 return BluetoothHeadset.STATE_DISCONNECTED; 619 } 620 return headset.mState; 621 } 622 public BluetoothDevice getCurrentHeadset() { 623 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 624 return getCurrentDevice(); 625 } 626 public boolean connectHeadset(BluetoothDevice device) { 627 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 628 "Need BLUETOOTH_ADMIN permission"); 629 synchronized (BluetoothHeadsetService.this) { 630 try { 631 return mBluetoothService.connectHeadset(device.getAddress()); 632 } catch (RemoteException e) { 633 Log.e(TAG, "connectHeadset"); 634 return false; 635 } 636 } 637 } 638 public void disconnectHeadset(BluetoothDevice device) { 639 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 640 "Need BLUETOOTH_ADMIN permission"); 641 synchronized (BluetoothHeadsetService.this) { 642 try { 643 mBluetoothService.disconnectHeadset(device.getAddress()); 644 } catch (RemoteException e) { 645 Log.e(TAG, "disconnectHeadset"); 646 } 647 } 648 } 649 public boolean isConnected(BluetoothDevice device) { 650 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 651 652 BluetoothRemoteHeadset headset = mRemoteHeadsets.get(device); 653 return headset != null && headset.mState == BluetoothHeadset.STATE_CONNECTED; 654 } 655 public boolean startVoiceRecognition() { 656 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 657 synchronized (BluetoothHeadsetService.this) { 658 BluetoothDevice device = getCurrentDevice(); 659 660 if (device == null || 661 mRemoteHeadsets.get(device).mState != BluetoothHeadset.STATE_CONNECTED) { 662 return false; 663 } 664 return mBtHandsfree.startVoiceRecognition(); 665 } 666 } 667 public boolean stopVoiceRecognition() { 668 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 669 synchronized (BluetoothHeadsetService.this) { 670 BluetoothDevice device = getCurrentDevice(); 671 672 if (device == null || 673 mRemoteHeadsets.get(device).mState != BluetoothHeadset.STATE_CONNECTED) { 674 return false; 675 } 676 677 return mBtHandsfree.stopVoiceRecognition(); 678 } 679 } 680 public int getBatteryUsageHint() { 681 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 682 683 return HeadsetBase.getAtInputCount(); 684 } 685 public int getPriority(BluetoothDevice device) { 686 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 687 "Need BLUETOOTH_ADMIN permission"); 688 synchronized (BluetoothHeadsetService.this) { 689 int priority = Settings.Secure.getInt(getContentResolver(), 690 Settings.Secure.getBluetoothHeadsetPriorityKey(device.getAddress()), 691 BluetoothHeadset.PRIORITY_UNDEFINED); 692 return priority; 693 } 694 } 695 696 public boolean setPriority(BluetoothDevice device, int priority) { 697 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 698 "Need BLUETOOTH_ADMIN permission"); 699 synchronized (BluetoothHeadsetService.this) { 700 if (!BluetoothAdapter.checkBluetoothAddress(device.getAddress())) { 701 return false; 702 } 703 if (priority < BluetoothHeadset.PRIORITY_OFF) { 704 return false; 705 } 706 Settings.Secure.putInt(getContentResolver(), 707 Settings.Secure.getBluetoothHeadsetPriorityKey(device.getAddress()), 708 priority); 709 if (DBG) log("Saved priority " + device + " = " + priority); 710 return true; 711 } 712 } 713 714 public boolean createIncomingConnect(BluetoothDevice device) { 715 synchronized (BluetoothHeadsetService.this) { 716 HeadsetBase headset; 717 setState(device, BluetoothHeadset.STATE_CONNECTING); 718 719 IncomingConnectionInfo info = mRemoteHeadsets.get(device).mIncomingInfo; 720 headset = new HeadsetBase(mPowerManager, mAdapter, device, 721 info.mSocketFd, info.mRfcommChan, 722 mConnectedStatusHandler); 723 724 mRemoteHeadsets.get(device).mHeadset = headset; 725 726 mConnectingStatusHandler.obtainMessage(RFCOMM_CONNECTED, headset).sendToTarget(); 727 return true; 728 } 729 } 730 731 public boolean rejectIncomingConnect(BluetoothDevice device) { 732 synchronized (BluetoothHeadsetService.this) { 733 BluetoothRemoteHeadset headset = mRemoteHeadsets.get(device); 734 if (headset != null) { 735 IncomingConnectionInfo info = headset.mIncomingInfo; 736 rejectIncomingConnection(info); 737 } else { 738 Log.e(TAG, "Error no record of remote headset"); 739 } 740 return true; 741 } 742 } 743 744 public boolean acceptIncomingConnect(BluetoothDevice device) { 745 synchronized (BluetoothHeadsetService.this) { 746 HeadsetBase headset; 747 BluetoothRemoteHeadset cachedHeadset = mRemoteHeadsets.get(device); 748 if (cachedHeadset == null) { 749 Log.e(TAG, "Cached Headset is Null in acceptIncomingConnect"); 750 return false; 751 } 752 IncomingConnectionInfo info = cachedHeadset.mIncomingInfo; 753 headset = new HeadsetBase(mPowerManager, mAdapter, device, 754 info.mSocketFd, info.mRfcommChan, mConnectedStatusHandler); 755 756 setState(device, BluetoothHeadset.STATE_CONNECTED, BluetoothHeadset.RESULT_SUCCESS); 757 758 cachedHeadset.mHeadset = headset; 759 mBtHandsfree.connectHeadset(headset, cachedHeadset.mHeadsetType); 760 761 if (DBG) log("Successfully used incoming connection"); 762 return true; 763 } 764 } 765 766 public boolean cancelConnectThread() { 767 synchronized (BluetoothHeadsetService.this) { 768 if (mConnectThread != null) { 769 // cancel the connection thread 770 mConnectThread.interrupt(); 771 try { 772 mConnectThread.join(); 773 } catch (InterruptedException e) { 774 Log.e(TAG, "Connection cancelled twice?", e); 775 } 776 mConnectThread = null; 777 } 778 return true; 779 } 780 } 781 782 public boolean connectHeadsetInternal(BluetoothDevice device) { 783 synchronized (BluetoothHeadsetService.this) { 784 BluetoothDevice currDevice = getCurrentDevice(); 785 if (currDevice == null) { 786 BluetoothRemoteHeadset headset = new BluetoothRemoteHeadset(); 787 mRemoteHeadsets.put(device, headset); 788 789 setState(device, BluetoothHeadset.STATE_CONNECTING); 790 if (device.getUuids() == null) { 791 // We might not have got the UUID change notification from 792 // Bluez yet, if we have just paired. Try after 1.5 secs. 793 Message msg = new Message(); 794 msg.what = CONNECT_HEADSET_DELAYED; 795 msg.obj = device; 796 mHandler.sendMessageDelayed(msg, 1500); 797 } else { 798 getSdpRecordsAndConnect(device); 799 } 800 return true; 801 } else { 802 Log.w(TAG, "connectHeadset(" + device + "): failed: already in state " + 803 mRemoteHeadsets.get(currDevice).mState + 804 " with headset " + currDevice); 805 } 806 return false; 807 } 808 } 809 810 public boolean disconnectHeadsetInternal(BluetoothDevice device) { 811 synchronized (BluetoothHeadsetService.this) { 812 BluetoothRemoteHeadset remoteHeadset = mRemoteHeadsets.get(device); 813 if (remoteHeadset == null) return false; 814 815 if (remoteHeadset.mState == BluetoothHeadset.STATE_CONNECTED) { 816 // Send a dummy battery level message to force headset 817 // out of sniff mode so that it will immediately notice 818 // the disconnection. We are currently sending it for 819 // handsfree only. 820 // TODO: Call hci_conn_enter_active_mode() from 821 // rfcomm_send_disc() in the kernel instead. 822 // See http://b/1716887 823 HeadsetBase headset = remoteHeadset.mHeadset; 824 if (remoteHeadset.mHeadsetType == BluetoothHandsfree.TYPE_HANDSFREE) { 825 headset.sendURC("+CIEV: 7,3"); 826 } 827 828 if (headset != null) { 829 headset.disconnect(); 830 headset = null; 831 } 832 setState(device, BluetoothHeadset.STATE_DISCONNECTED, 833 BluetoothHeadset.RESULT_CANCELED, 834 BluetoothHeadset.LOCAL_DISCONNECT); 835 return true; 836 } else if (remoteHeadset.mState == BluetoothHeadset.STATE_CONNECTING) { 837 // The state machine would have canceled the connect thread. 838 // Just set the state here. 839 setState(device, BluetoothHeadset.STATE_DISCONNECTED, 840 BluetoothHeadset.RESULT_CANCELED, 841 BluetoothHeadset.LOCAL_DISCONNECT); 842 return true; 843 } 844 return false; 845 } 846 } 847 }; 848 849 @Override onDestroy()850 public void onDestroy() { 851 super.onDestroy(); 852 if (DBG) log("Stopping BluetoothHeadsetService"); 853 unregisterReceiver(mBluetoothReceiver); 854 mBtHandsfree.onBluetoothDisabled(); 855 mAg.stop(); 856 sHasStarted = false; 857 if (getCurrentDevice() != null) { 858 setState(getCurrentDevice(), BluetoothHeadset.STATE_DISCONNECTED, 859 BluetoothHeadset.RESULT_CANCELED, 860 BluetoothHeadset.LOCAL_DISCONNECT); 861 } 862 } 863 864 865 log(String msg)866 private static void log(String msg) { 867 Log.d(TAG, msg); 868 } 869 } 870