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