1 /* 2 * Copyright (C) 2008 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 android.bluetooth; 18 19 import android.annotation.SdkConstant; 20 import android.annotation.SdkConstant.SdkConstantType; 21 import android.content.ComponentName; 22 import android.content.Context; 23 import android.content.Intent; 24 import android.content.ServiceConnection; 25 import android.os.IBinder; 26 import android.os.RemoteException; 27 import android.util.Log; 28 29 import java.util.ArrayList; 30 import java.util.List; 31 32 /** 33 * Public API for controlling the Bluetooth Headset Service. This includes both 34 * Bluetooth Headset and Handsfree (v1.5) profiles. 35 * 36 * <p>BluetoothHeadset is a proxy object for controlling the Bluetooth Headset 37 * Service via IPC. 38 * 39 * <p> Use {@link BluetoothAdapter#getProfileProxy} to get 40 * the BluetoothHeadset proxy object. Use 41 * {@link BluetoothAdapter#closeProfileProxy} to close the service connection. 42 * 43 * <p> Android only supports one connected Bluetooth Headset at a time. 44 * Each method is protected with its appropriate permission. 45 */ 46 public final class BluetoothHeadset implements BluetoothProfile { 47 private static final String TAG = "BluetoothHeadset"; 48 private static final boolean DBG = false; 49 50 /** 51 * Intent used to broadcast the change in connection state of the Headset 52 * profile. 53 * 54 * <p>This intent will have 3 extras: 55 * <ul> 56 * <li> {@link #EXTRA_STATE} - The current state of the profile. </li> 57 * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile. </li> 58 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li> 59 * </ul> 60 * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of 61 * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, 62 * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}. 63 * 64 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to 65 * receive. 66 */ 67 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 68 public static final String ACTION_CONNECTION_STATE_CHANGED = 69 "android.bluetooth.headset.profile.action.CONNECTION_STATE_CHANGED"; 70 71 /** 72 * Intent used to broadcast the change in the Audio Connection state of the 73 * A2DP profile. 74 * 75 * <p>This intent will have 3 extras: 76 * <ul> 77 * <li> {@link #EXTRA_STATE} - The current state of the profile. </li> 78 * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile. </li> 79 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li> 80 * </ul> 81 * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of 82 * {@link #STATE_AUDIO_CONNECTED}, {@link #STATE_AUDIO_DISCONNECTED}, 83 * 84 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission 85 * to receive. 86 */ 87 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 88 public static final String ACTION_AUDIO_STATE_CHANGED = 89 "android.bluetooth.headset.profile.action.AUDIO_STATE_CHANGED"; 90 91 92 /** 93 * Intent used to broadcast that the headset has posted a 94 * vendor-specific event. 95 * 96 * <p>This intent will have 4 extras and 1 category. 97 * <ul> 98 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote Bluetooth Device 99 * </li> 100 * <li> {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD} - The vendor 101 * specific command </li> 102 * <li> {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE} - The AT 103 * command type which can be one of {@link #AT_CMD_TYPE_READ}, 104 * {@link #AT_CMD_TYPE_TEST}, or {@link #AT_CMD_TYPE_SET}, 105 * {@link #AT_CMD_TYPE_BASIC},{@link #AT_CMD_TYPE_ACTION}. </li> 106 * <li> {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS} - Command 107 * arguments. </li> 108 * </ul> 109 * 110 *<p> The category is the Company ID of the vendor defining the 111 * vendor-specific command. {@link BluetoothAssignedNumbers} 112 * 113 * For example, for Plantronics specific events 114 * Category will be {@link #VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY}.55 115 * 116 * <p> For example, an AT+XEVENT=foo,3 will get translated into 117 * <ul> 118 * <li> EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD = +XEVENT </li> 119 * <li> EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE = AT_CMD_TYPE_SET </li> 120 * <li> EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS = foo, 3 </li> 121 * </ul> 122 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission 123 * to receive. 124 */ 125 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 126 public static final String ACTION_VENDOR_SPECIFIC_HEADSET_EVENT = 127 "android.bluetooth.headset.action.VENDOR_SPECIFIC_HEADSET_EVENT"; 128 129 /** 130 * A String extra field in {@link #ACTION_VENDOR_SPECIFIC_HEADSET_EVENT} 131 * intents that contains the name of the vendor-specific command. 132 */ 133 public static final String EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD = 134 "android.bluetooth.headset.extra.VENDOR_SPECIFIC_HEADSET_EVENT_CMD"; 135 136 /** 137 * An int extra field in {@link #ACTION_VENDOR_SPECIFIC_HEADSET_EVENT} 138 * intents that contains the AT command type of the vendor-specific command. 139 */ 140 public static final String EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE = 141 "android.bluetooth.headset.extra.VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE"; 142 143 /** 144 * AT command type READ used with 145 * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE} 146 * For example, AT+VGM?. There are no arguments for this command type. 147 */ 148 public static final int AT_CMD_TYPE_READ = 0; 149 150 /** 151 * AT command type TEST used with 152 * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE} 153 * For example, AT+VGM=?. There are no arguments for this command type. 154 */ 155 public static final int AT_CMD_TYPE_TEST = 1; 156 157 /** 158 * AT command type SET used with 159 * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE} 160 * For example, AT+VGM=<args>. 161 */ 162 public static final int AT_CMD_TYPE_SET = 2; 163 164 /** 165 * AT command type BASIC used with 166 * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE} 167 * For example, ATD. Single character commands and everything following the 168 * character are arguments. 169 */ 170 public static final int AT_CMD_TYPE_BASIC = 3; 171 172 /** 173 * AT command type ACTION used with 174 * {@link #EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE} 175 * For example, AT+CHUP. There are no arguments for action commands. 176 */ 177 public static final int AT_CMD_TYPE_ACTION = 4; 178 179 /** 180 * A Parcelable String array extra field in 181 * {@link #ACTION_VENDOR_SPECIFIC_HEADSET_EVENT} intents that contains 182 * the arguments to the vendor-specific command. 183 */ 184 public static final String EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS = 185 "android.bluetooth.headset.extra.VENDOR_SPECIFIC_HEADSET_EVENT_ARGS"; 186 187 /** 188 * The intent category to be used with {@link #ACTION_VENDOR_SPECIFIC_HEADSET_EVENT} 189 * for the companyId 190 */ 191 public static final String VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY = 192 "android.bluetooth.headset.intent.category.companyid"; 193 194 /** 195 * Headset state when SCO audio is not connected. 196 * This state can be one of 197 * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of 198 * {@link #ACTION_AUDIO_STATE_CHANGED} intent. 199 */ 200 public static final int STATE_AUDIO_DISCONNECTED = 10; 201 202 /** 203 * Headset state when SCO audio is connecting. 204 * This state can be one of 205 * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of 206 * {@link #ACTION_AUDIO_STATE_CHANGED} intent. 207 */ 208 public static final int STATE_AUDIO_CONNECTING = 11; 209 210 /** 211 * Headset state when SCO audio is connected. 212 * This state can be one of 213 * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of 214 * {@link #ACTION_AUDIO_STATE_CHANGED} intent. 215 */ 216 public static final int STATE_AUDIO_CONNECTED = 12; 217 218 219 private Context mContext; 220 private ServiceListener mServiceListener; 221 private IBluetoothHeadset mService; 222 BluetoothAdapter mAdapter; 223 224 /** 225 * Create a BluetoothHeadset proxy object. 226 */ BluetoothHeadset(Context context, ServiceListener l)227 /*package*/ BluetoothHeadset(Context context, ServiceListener l) { 228 mContext = context; 229 mServiceListener = l; 230 mAdapter = BluetoothAdapter.getDefaultAdapter(); 231 if (!context.bindService(new Intent(IBluetoothHeadset.class.getName()), mConnection, 0)) { 232 Log.e(TAG, "Could not bind to Bluetooth Headset Service"); 233 } 234 } 235 236 /** 237 * Close the connection to the backing service. 238 * Other public functions of BluetoothHeadset will return default error 239 * results once close() has been called. Multiple invocations of close() 240 * are ok. 241 */ close()242 /*package*/ synchronized void close() { 243 if (DBG) log("close()"); 244 if (mConnection != null) { 245 mContext.unbindService(mConnection); 246 mConnection = null; 247 } 248 } 249 250 /** 251 * Initiate connection to a profile of the remote bluetooth device. 252 * 253 * <p> Currently, the system supports only 1 connection to the 254 * headset/handsfree profile. The API will automatically disconnect connected 255 * devices before connecting. 256 * 257 * <p> This API returns false in scenarios like the profile on the 258 * device is already connected or Bluetooth is not turned on. 259 * When this API returns true, it is guaranteed that 260 * connection state intent for the profile will be broadcasted with 261 * the state. Users can get the connection state of the profile 262 * from this intent. 263 * 264 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} 265 * permission. 266 * 267 * @param device Remote Bluetooth Device 268 * @return false on immediate error, 269 * true otherwise 270 * @hide 271 */ connect(BluetoothDevice device)272 public boolean connect(BluetoothDevice device) { 273 if (DBG) log("connect(" + device + ")"); 274 if (mService != null && isEnabled() && 275 isValidDevice(device)) { 276 try { 277 return mService.connect(device); 278 } catch (RemoteException e) { 279 Log.e(TAG, Log.getStackTraceString(new Throwable())); 280 return false; 281 } 282 } 283 if (mService == null) Log.w(TAG, "Proxy not attached to service"); 284 return false; 285 } 286 287 /** 288 * Initiate disconnection from a profile 289 * 290 * <p> This API will return false in scenarios like the profile on the 291 * Bluetooth device is not in connected state etc. When this API returns, 292 * true, it is guaranteed that the connection state change 293 * intent will be broadcasted with the state. Users can get the 294 * disconnection state of the profile from this intent. 295 * 296 * <p> If the disconnection is initiated by a remote device, the state 297 * will transition from {@link #STATE_CONNECTED} to 298 * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the 299 * host (local) device the state will transition from 300 * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to 301 * state {@link #STATE_DISCONNECTED}. The transition to 302 * {@link #STATE_DISCONNECTING} can be used to distinguish between the 303 * two scenarios. 304 * 305 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} 306 * permission. 307 * 308 * @param device Remote Bluetooth Device 309 * @return false on immediate error, 310 * true otherwise 311 * @hide 312 */ disconnect(BluetoothDevice device)313 public boolean disconnect(BluetoothDevice device) { 314 if (DBG) log("disconnect(" + device + ")"); 315 if (mService != null && isEnabled() && 316 isValidDevice(device)) { 317 try { 318 return mService.disconnect(device); 319 } catch (RemoteException e) { 320 Log.e(TAG, Log.getStackTraceString(new Throwable())); 321 return false; 322 } 323 } 324 if (mService == null) Log.w(TAG, "Proxy not attached to service"); 325 return false; 326 } 327 328 /** 329 * {@inheritDoc} 330 */ getConnectedDevices()331 public List<BluetoothDevice> getConnectedDevices() { 332 if (DBG) log("getConnectedDevices()"); 333 if (mService != null && isEnabled()) { 334 try { 335 return mService.getConnectedDevices(); 336 } catch (RemoteException e) { 337 Log.e(TAG, Log.getStackTraceString(new Throwable())); 338 return new ArrayList<BluetoothDevice>(); 339 } 340 } 341 if (mService == null) Log.w(TAG, "Proxy not attached to service"); 342 return new ArrayList<BluetoothDevice>(); 343 } 344 345 /** 346 * {@inheritDoc} 347 */ getDevicesMatchingConnectionStates(int[] states)348 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 349 if (DBG) log("getDevicesMatchingStates()"); 350 if (mService != null && isEnabled()) { 351 try { 352 return mService.getDevicesMatchingConnectionStates(states); 353 } catch (RemoteException e) { 354 Log.e(TAG, Log.getStackTraceString(new Throwable())); 355 return new ArrayList<BluetoothDevice>(); 356 } 357 } 358 if (mService == null) Log.w(TAG, "Proxy not attached to service"); 359 return new ArrayList<BluetoothDevice>(); 360 } 361 362 /** 363 * {@inheritDoc} 364 */ getConnectionState(BluetoothDevice device)365 public int getConnectionState(BluetoothDevice device) { 366 if (DBG) log("getConnectionState(" + device + ")"); 367 if (mService != null && isEnabled() && 368 isValidDevice(device)) { 369 try { 370 return mService.getConnectionState(device); 371 } catch (RemoteException e) { 372 Log.e(TAG, Log.getStackTraceString(new Throwable())); 373 return BluetoothProfile.STATE_DISCONNECTED; 374 } 375 } 376 if (mService == null) Log.w(TAG, "Proxy not attached to service"); 377 return BluetoothProfile.STATE_DISCONNECTED; 378 } 379 380 /** 381 * Set priority of the profile 382 * 383 * <p> The device should already be paired. 384 * Priority can be one of {@link #PRIORITY_ON} or 385 * {@link #PRIORITY_OFF}, 386 * 387 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} 388 * permission. 389 * 390 * @param device Paired bluetooth device 391 * @param priority 392 * @return true if priority is set, false on error 393 * @hide 394 */ setPriority(BluetoothDevice device, int priority)395 public boolean setPriority(BluetoothDevice device, int priority) { 396 if (DBG) log("setPriority(" + device + ", " + priority + ")"); 397 if (mService != null && isEnabled() && 398 isValidDevice(device)) { 399 if (priority != BluetoothProfile.PRIORITY_OFF && 400 priority != BluetoothProfile.PRIORITY_ON) { 401 return false; 402 } 403 try { 404 return mService.setPriority(device, priority); 405 } catch (RemoteException e) { 406 Log.e(TAG, Log.getStackTraceString(new Throwable())); 407 return false; 408 } 409 } 410 if (mService == null) Log.w(TAG, "Proxy not attached to service"); 411 return false; 412 } 413 414 /** 415 * Get the priority of the profile. 416 * 417 * <p> The priority can be any of: 418 * {@link #PRIORITY_AUTO_CONNECT}, {@link #PRIORITY_OFF}, 419 * {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED} 420 * 421 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 422 * 423 * @param device Bluetooth device 424 * @return priority of the device 425 * @hide 426 */ getPriority(BluetoothDevice device)427 public int getPriority(BluetoothDevice device) { 428 if (DBG) log("getPriority(" + device + ")"); 429 if (mService != null && isEnabled() && 430 isValidDevice(device)) { 431 try { 432 return mService.getPriority(device); 433 } catch (RemoteException e) { 434 Log.e(TAG, Log.getStackTraceString(new Throwable())); 435 return PRIORITY_OFF; 436 } 437 } 438 if (mService == null) Log.w(TAG, "Proxy not attached to service"); 439 return PRIORITY_OFF; 440 } 441 442 /** 443 * Start Bluetooth voice recognition. This methods sends the voice 444 * recognition AT command to the headset and establishes the 445 * audio connection. 446 * 447 * <p> Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}. 448 * If this function returns true, this intent will be broadcasted with 449 * {@link #EXTRA_STATE} set to {@link #STATE_AUDIO_CONNECTING}. 450 * 451 * <p> {@link #EXTRA_STATE} will transition from 452 * {@link #STATE_AUDIO_CONNECTING} to {@link #STATE_AUDIO_CONNECTED} when 453 * audio connection is established and to {@link #STATE_AUDIO_DISCONNECTED} 454 * in case of failure to establish the audio connection. 455 * 456 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 457 * 458 * @param device Bluetooth headset 459 * @return false if there is no headset connected of if the 460 * connected headset doesn't support voice recognition 461 * or on error, true otherwise 462 */ startVoiceRecognition(BluetoothDevice device)463 public boolean startVoiceRecognition(BluetoothDevice device) { 464 if (DBG) log("startVoiceRecognition()"); 465 if (mService != null && isEnabled() && 466 isValidDevice(device)) { 467 try { 468 return mService.startVoiceRecognition(device); 469 } catch (RemoteException e) { 470 Log.e(TAG, Log.getStackTraceString(new Throwable())); 471 } 472 } 473 if (mService == null) Log.w(TAG, "Proxy not attached to service"); 474 return false; 475 } 476 477 /** 478 * Stop Bluetooth Voice Recognition mode, and shut down the 479 * Bluetooth audio path. 480 * 481 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 482 * 483 * @param device Bluetooth headset 484 * @return false if there is no headset connected 485 * or on error, true otherwise 486 */ stopVoiceRecognition(BluetoothDevice device)487 public boolean stopVoiceRecognition(BluetoothDevice device) { 488 if (DBG) log("stopVoiceRecognition()"); 489 if (mService != null && isEnabled() && 490 isValidDevice(device)) { 491 try { 492 return mService.stopVoiceRecognition(device); 493 } catch (RemoteException e) { 494 Log.e(TAG, Log.getStackTraceString(new Throwable())); 495 } 496 } 497 if (mService == null) Log.w(TAG, "Proxy not attached to service"); 498 return false; 499 } 500 501 /** 502 * Check if Bluetooth SCO audio is connected. 503 * 504 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. 505 * 506 * @param device Bluetooth headset 507 * @return true if SCO is connected, 508 * false otherwise or on error 509 */ isAudioConnected(BluetoothDevice device)510 public boolean isAudioConnected(BluetoothDevice device) { 511 if (DBG) log("isAudioConnected()"); 512 if (mService != null && isEnabled() && 513 isValidDevice(device)) { 514 try { 515 return mService.isAudioConnected(device); 516 } catch (RemoteException e) { 517 Log.e(TAG, Log.getStackTraceString(new Throwable())); 518 } 519 } 520 if (mService == null) Log.w(TAG, "Proxy not attached to service"); 521 return false; 522 } 523 524 /** 525 * Get battery usage hint for Bluetooth Headset service. 526 * This is a monotonically increasing integer. Wraps to 0 at 527 * Integer.MAX_INT, and at boot. 528 * Current implementation returns the number of AT commands handled since 529 * boot. This is a good indicator for spammy headset/handsfree units that 530 * can keep the device awake by polling for cellular status updates. As a 531 * rule of thumb, each AT command prevents the CPU from sleeping for 500 ms 532 * 533 * @param device the bluetooth headset. 534 * @return monotonically increasing battery usage hint, or a negative error 535 * code on error 536 * @hide 537 */ getBatteryUsageHint(BluetoothDevice device)538 public int getBatteryUsageHint(BluetoothDevice device) { 539 if (DBG) log("getBatteryUsageHint()"); 540 if (mService != null && isEnabled() && 541 isValidDevice(device)) { 542 try { 543 return mService.getBatteryUsageHint(device); 544 } catch (RemoteException e) { 545 Log.e(TAG, Log.getStackTraceString(new Throwable())); 546 } 547 } 548 if (mService == null) Log.w(TAG, "Proxy not attached to service"); 549 return -1; 550 } 551 552 /** 553 * Indicates if current platform supports voice dialing over bluetooth SCO. 554 * 555 * @return true if voice dialing over bluetooth is supported, false otherwise. 556 * @hide 557 */ isBluetoothVoiceDialingEnabled(Context context)558 public static boolean isBluetoothVoiceDialingEnabled(Context context) { 559 return context.getResources().getBoolean( 560 com.android.internal.R.bool.config_bluetooth_sco_off_call); 561 } 562 563 /** 564 * Cancel the outgoing connection. 565 * Note: This is an internal function and shouldn't be exposed 566 * 567 * @hide 568 */ cancelConnectThread()569 public boolean cancelConnectThread() { 570 if (DBG) log("cancelConnectThread"); 571 if (mService != null && isEnabled()) { 572 try { 573 return mService.cancelConnectThread(); 574 } catch (RemoteException e) {Log.e(TAG, e.toString());} 575 } else { 576 Log.w(TAG, "Proxy not attached to service"); 577 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); 578 } 579 return false; 580 } 581 582 /** 583 * Accept the incoming connection. 584 * Note: This is an internal function and shouldn't be exposed 585 * 586 * @hide 587 */ acceptIncomingConnect(BluetoothDevice device)588 public boolean acceptIncomingConnect(BluetoothDevice device) { 589 if (DBG) log("acceptIncomingConnect"); 590 if (mService != null && isEnabled()) { 591 try { 592 return mService.acceptIncomingConnect(device); 593 } catch (RemoteException e) {Log.e(TAG, e.toString());} 594 } else { 595 Log.w(TAG, "Proxy not attached to service"); 596 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); 597 } 598 return false; 599 } 600 601 /** 602 * Create the connect thread for the incoming connection. 603 * Note: This is an internal function and shouldn't be exposed 604 * 605 * @hide 606 */ createIncomingConnect(BluetoothDevice device)607 public boolean createIncomingConnect(BluetoothDevice device) { 608 if (DBG) log("createIncomingConnect"); 609 if (mService != null && isEnabled()) { 610 try { 611 return mService.createIncomingConnect(device); 612 } catch (RemoteException e) {Log.e(TAG, e.toString());} 613 } else { 614 Log.w(TAG, "Proxy not attached to service"); 615 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); 616 } 617 return false; 618 } 619 620 /** 621 * Reject the incoming connection. 622 * @hide 623 */ rejectIncomingConnect(BluetoothDevice device)624 public boolean rejectIncomingConnect(BluetoothDevice device) { 625 if (DBG) log("rejectIncomingConnect"); 626 if (mService != null) { 627 try { 628 return mService.rejectIncomingConnect(device); 629 } catch (RemoteException e) {Log.e(TAG, e.toString());} 630 } else { 631 Log.w(TAG, "Proxy not attached to service"); 632 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); 633 } 634 return false; 635 } 636 637 /** 638 * Connect to a Bluetooth Headset. 639 * Note: This is an internal function and shouldn't be exposed 640 * 641 * @hide 642 */ connectHeadsetInternal(BluetoothDevice device)643 public boolean connectHeadsetInternal(BluetoothDevice device) { 644 if (DBG) log("connectHeadsetInternal"); 645 if (mService != null && isEnabled()) { 646 try { 647 return mService.connectHeadsetInternal(device); 648 } catch (RemoteException e) {Log.e(TAG, e.toString());} 649 } else { 650 Log.w(TAG, "Proxy not attached to service"); 651 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); 652 } 653 return false; 654 } 655 656 /** 657 * Disconnect a Bluetooth Headset. 658 * Note: This is an internal function and shouldn't be exposed 659 * 660 * @hide 661 */ disconnectHeadsetInternal(BluetoothDevice device)662 public boolean disconnectHeadsetInternal(BluetoothDevice device) { 663 if (DBG) log("disconnectHeadsetInternal"); 664 if (mService != null && !isDisabled()) { 665 try { 666 return mService.disconnectHeadsetInternal(device); 667 } catch (RemoteException e) {Log.e(TAG, e.toString());} 668 } else { 669 Log.w(TAG, "Proxy not attached to service"); 670 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); 671 } 672 return false; 673 } 674 675 /** 676 * Set the audio state of the Headset. 677 * Note: This is an internal function and shouldn't be exposed 678 * 679 * @hide 680 */ setAudioState(BluetoothDevice device, int state)681 public boolean setAudioState(BluetoothDevice device, int state) { 682 if (DBG) log("setAudioState"); 683 if (mService != null && !isDisabled()) { 684 try { 685 return mService.setAudioState(device, state); 686 } catch (RemoteException e) {Log.e(TAG, e.toString());} 687 } else { 688 Log.w(TAG, "Proxy not attached to service"); 689 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); 690 } 691 return false; 692 } 693 694 /** 695 * Get the current audio state of the Headset. 696 * Note: This is an internal function and shouldn't be exposed 697 * 698 * @hide 699 */ getAudioState(BluetoothDevice device)700 public int getAudioState(BluetoothDevice device) { 701 if (DBG) log("getAudioState"); 702 if (mService != null && !isDisabled()) { 703 try { 704 return mService.getAudioState(device); 705 } catch (RemoteException e) {Log.e(TAG, e.toString());} 706 } else { 707 Log.w(TAG, "Proxy not attached to service"); 708 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); 709 } 710 return BluetoothHeadset.STATE_AUDIO_DISCONNECTED; 711 } 712 713 /** 714 * Initiates a SCO channel connection with the headset (if connected). 715 * Also initiates a virtual voice call for Handsfree devices as many devices 716 * do not accept SCO audio without a call. 717 * This API allows the handsfree device to be used for routing non-cellular 718 * call audio. 719 * 720 * @param device Remote Bluetooth Device 721 * @return true if successful, false if there was some error. 722 * @hide 723 */ startScoUsingVirtualVoiceCall(BluetoothDevice device)724 public boolean startScoUsingVirtualVoiceCall(BluetoothDevice device) { 725 if (DBG) log("startScoUsingVirtualVoiceCall()"); 726 if (mService != null && isEnabled() && isValidDevice(device)) { 727 try { 728 return mService.startScoUsingVirtualVoiceCall(device); 729 } catch (RemoteException e) { 730 Log.e(TAG, e.toString()); 731 } 732 } else { 733 Log.w(TAG, "Proxy not attached to service"); 734 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); 735 } 736 return false; 737 } 738 739 /** 740 * Terminates an ongoing SCO connection and the associated virtual 741 * call. 742 * 743 * @param device Remote Bluetooth Device 744 * @return true if successful, false if there was some error. 745 * @hide 746 */ stopScoUsingVirtualVoiceCall(BluetoothDevice device)747 public boolean stopScoUsingVirtualVoiceCall(BluetoothDevice device) { 748 if (DBG) log("stopScoUsingVirtualVoiceCall()"); 749 if (mService != null && isEnabled() && isValidDevice(device)) { 750 try { 751 return mService.stopScoUsingVirtualVoiceCall(device); 752 } catch (RemoteException e) { 753 Log.e(TAG, e.toString()); 754 } 755 } else { 756 Log.w(TAG, "Proxy not attached to service"); 757 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); 758 } 759 return false; 760 } 761 762 private ServiceConnection mConnection = new ServiceConnection() { 763 public void onServiceConnected(ComponentName className, IBinder service) { 764 if (DBG) Log.d(TAG, "Proxy object connected"); 765 mService = IBluetoothHeadset.Stub.asInterface(service); 766 767 if (mServiceListener != null) { 768 mServiceListener.onServiceConnected(BluetoothProfile.HEADSET, BluetoothHeadset.this); 769 } 770 } 771 public void onServiceDisconnected(ComponentName className) { 772 if (DBG) Log.d(TAG, "Proxy object disconnected"); 773 mService = null; 774 if (mServiceListener != null) { 775 mServiceListener.onServiceDisconnected(BluetoothProfile.HEADSET); 776 } 777 } 778 }; 779 isEnabled()780 private boolean isEnabled() { 781 if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true; 782 return false; 783 } 784 isDisabled()785 private boolean isDisabled() { 786 if (mAdapter.getState() == BluetoothAdapter.STATE_OFF) return true; 787 return false; 788 } 789 isValidDevice(BluetoothDevice device)790 private boolean isValidDevice(BluetoothDevice device) { 791 if (device == null) return false; 792 793 if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true; 794 return false; 795 } 796 log(String msg)797 private static void log(String msg) { 798 Log.d(TAG, msg); 799 } 800 } 801