1 /* 2 * Copyright (C) 2014 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.UnsupportedAppUsage; 20 import android.content.Context; 21 import android.os.Binder; 22 import android.os.Bundle; 23 import android.os.IBinder; 24 import android.os.RemoteException; 25 import android.util.Log; 26 27 import java.util.ArrayList; 28 import java.util.List; 29 30 /** 31 * Public API to control Hands Free Profile (HFP role only). 32 * <p> 33 * This class defines methods that shall be used by application to manage profile 34 * connection, calls states and calls actions. 35 * <p> 36 * 37 * @hide 38 */ 39 public final class BluetoothHeadsetClient implements BluetoothProfile { 40 private static final String TAG = "BluetoothHeadsetClient"; 41 private static final boolean DBG = true; 42 private static final boolean VDBG = false; 43 44 /** 45 * Intent sent whenever connection to remote changes. 46 * 47 * <p>It includes two extras: 48 * <code>BluetoothProfile.EXTRA_PREVIOUS_STATE</code> 49 * and <code>BluetoothProfile.EXTRA_STATE</code>, which 50 * are mandatory. 51 * <p>There are also non mandatory feature extras: 52 * {@link #EXTRA_AG_FEATURE_3WAY_CALLING}, 53 * {@link #EXTRA_AG_FEATURE_VOICE_RECOGNITION}, 54 * {@link #EXTRA_AG_FEATURE_ATTACH_NUMBER_TO_VT}, 55 * {@link #EXTRA_AG_FEATURE_REJECT_CALL}, 56 * {@link #EXTRA_AG_FEATURE_ECC}, 57 * {@link #EXTRA_AG_FEATURE_RESPONSE_AND_HOLD}, 58 * {@link #EXTRA_AG_FEATURE_ACCEPT_HELD_OR_WAITING_CALL}, 59 * {@link #EXTRA_AG_FEATURE_RELEASE_HELD_OR_WAITING_CALL}, 60 * {@link #EXTRA_AG_FEATURE_RELEASE_AND_ACCEPT}, 61 * {@link #EXTRA_AG_FEATURE_MERGE}, 62 * {@link #EXTRA_AG_FEATURE_MERGE_AND_DETACH}, 63 * sent as boolean values only when <code>EXTRA_STATE</code> 64 * is set to <code>STATE_CONNECTED</code>.</p> 65 * 66 * <p>Note that features supported by AG are being sent as 67 * booleans with value <code>true</code>, 68 * and not supported ones are <strong>not</strong> being sent at all.</p> 69 */ 70 public static final String ACTION_CONNECTION_STATE_CHANGED = 71 "android.bluetooth.headsetclient.profile.action.CONNECTION_STATE_CHANGED"; 72 73 /** 74 * Intent sent whenever audio state changes. 75 * 76 * <p>It includes two mandatory extras: 77 * {@link BluetoothProfile#EXTRA_STATE}, 78 * {@link BluetoothProfile#EXTRA_PREVIOUS_STATE}, 79 * with possible values: 80 * {@link #STATE_AUDIO_CONNECTING}, 81 * {@link #STATE_AUDIO_CONNECTED}, 82 * {@link #STATE_AUDIO_DISCONNECTED}</p> 83 * <p>When <code>EXTRA_STATE</code> is set 84 * to </code>STATE_AUDIO_CONNECTED</code>, 85 * it also includes {@link #EXTRA_AUDIO_WBS} 86 * indicating wide band speech support.</p> 87 */ 88 public static final String ACTION_AUDIO_STATE_CHANGED = 89 "android.bluetooth.headsetclient.profile.action.AUDIO_STATE_CHANGED"; 90 91 /** 92 * Intent sending updates of the Audio Gateway state. 93 * Each extra is being sent only when value it 94 * represents has been changed recently on AG. 95 * <p>It can contain one or more of the following extras: 96 * {@link #EXTRA_NETWORK_STATUS}, 97 * {@link #EXTRA_NETWORK_SIGNAL_STRENGTH}, 98 * {@link #EXTRA_NETWORK_ROAMING}, 99 * {@link #EXTRA_BATTERY_LEVEL}, 100 * {@link #EXTRA_OPERATOR_NAME}, 101 * {@link #EXTRA_VOICE_RECOGNITION}, 102 * {@link #EXTRA_IN_BAND_RING}</p> 103 */ 104 public static final String ACTION_AG_EVENT = 105 "android.bluetooth.headsetclient.profile.action.AG_EVENT"; 106 107 /** 108 * Intent sent whenever state of a call changes. 109 * 110 * <p>It includes: 111 * {@link #EXTRA_CALL}, 112 * with value of {@link BluetoothHeadsetClientCall} instance, 113 * representing actual call state.</p> 114 */ 115 public static final String ACTION_CALL_CHANGED = 116 "android.bluetooth.headsetclient.profile.action.AG_CALL_CHANGED"; 117 118 /** 119 * Intent that notifies about the result of the last issued action. 120 * Please note that not every action results in explicit action result code being sent. 121 * Instead other notifications about new Audio Gateway state might be sent, 122 * like <code>ACTION_AG_EVENT</code> with <code>EXTRA_VOICE_RECOGNITION</code> value 123 * when for example user started voice recognition from HF unit. 124 */ 125 public static final String ACTION_RESULT = 126 "android.bluetooth.headsetclient.profile.action.RESULT"; 127 128 /** 129 * Intent that notifies about the number attached to the last voice tag 130 * recorded on AG. 131 * 132 * <p>It contains: 133 * {@link #EXTRA_NUMBER}, 134 * with a <code>String</code> value representing phone number.</p> 135 */ 136 public static final String ACTION_LAST_VTAG = 137 "android.bluetooth.headsetclient.profile.action.LAST_VTAG"; 138 139 public static final int STATE_AUDIO_DISCONNECTED = 0; 140 public static final int STATE_AUDIO_CONNECTING = 1; 141 public static final int STATE_AUDIO_CONNECTED = 2; 142 143 /** 144 * Extra with information if connected audio is WBS. 145 * <p>Possible values: <code>true</code>, 146 * <code>false</code>.</p> 147 */ 148 public static final String EXTRA_AUDIO_WBS = 149 "android.bluetooth.headsetclient.extra.AUDIO_WBS"; 150 151 /** 152 * Extra for AG_EVENT indicates network status. 153 * <p>Value: 0 - network unavailable, 154 * 1 - network available </p> 155 */ 156 public static final String EXTRA_NETWORK_STATUS = 157 "android.bluetooth.headsetclient.extra.NETWORK_STATUS"; 158 /** 159 * Extra for AG_EVENT intent indicates network signal strength. 160 * <p>Value: <code>Integer</code> representing signal strength.</p> 161 */ 162 public static final String EXTRA_NETWORK_SIGNAL_STRENGTH = 163 "android.bluetooth.headsetclient.extra.NETWORK_SIGNAL_STRENGTH"; 164 /** 165 * Extra for AG_EVENT intent indicates roaming state. 166 * <p>Value: 0 - no roaming 167 * 1 - active roaming</p> 168 */ 169 public static final String EXTRA_NETWORK_ROAMING = 170 "android.bluetooth.headsetclient.extra.NETWORK_ROAMING"; 171 /** 172 * Extra for AG_EVENT intent indicates the battery level. 173 * <p>Value: <code>Integer</code> representing signal strength.</p> 174 */ 175 public static final String EXTRA_BATTERY_LEVEL = 176 "android.bluetooth.headsetclient.extra.BATTERY_LEVEL"; 177 /** 178 * Extra for AG_EVENT intent indicates operator name. 179 * <p>Value: <code>String</code> representing operator name.</p> 180 */ 181 public static final String EXTRA_OPERATOR_NAME = 182 "android.bluetooth.headsetclient.extra.OPERATOR_NAME"; 183 /** 184 * Extra for AG_EVENT intent indicates voice recognition state. 185 * <p>Value: 186 * 0 - voice recognition stopped, 187 * 1 - voice recognition started.</p> 188 */ 189 public static final String EXTRA_VOICE_RECOGNITION = 190 "android.bluetooth.headsetclient.extra.VOICE_RECOGNITION"; 191 /** 192 * Extra for AG_EVENT intent indicates in band ring state. 193 * <p>Value: 194 * 0 - in band ring tone not supported, or 195 * 1 - in band ring tone supported.</p> 196 */ 197 public static final String EXTRA_IN_BAND_RING = 198 "android.bluetooth.headsetclient.extra.IN_BAND_RING"; 199 200 /** 201 * Extra for AG_EVENT intent indicates subscriber info. 202 * <p>Value: <code>String</code> containing subscriber information.</p> 203 */ 204 public static final String EXTRA_SUBSCRIBER_INFO = 205 "android.bluetooth.headsetclient.extra.SUBSCRIBER_INFO"; 206 207 /** 208 * Extra for AG_CALL_CHANGED intent indicates the 209 * {@link BluetoothHeadsetClientCall} object that has changed. 210 */ 211 public static final String EXTRA_CALL = 212 "android.bluetooth.headsetclient.extra.CALL"; 213 214 /** 215 * Extra for ACTION_LAST_VTAG intent. 216 * <p>Value: <code>String</code> representing phone number 217 * corresponding to last voice tag recorded on AG</p> 218 */ 219 public static final String EXTRA_NUMBER = 220 "android.bluetooth.headsetclient.extra.NUMBER"; 221 222 /** 223 * Extra for ACTION_RESULT intent that shows the result code of 224 * last issued action. 225 * <p>Possible results: 226 * {@link #ACTION_RESULT_OK}, 227 * {@link #ACTION_RESULT_ERROR}, 228 * {@link #ACTION_RESULT_ERROR_NO_CARRIER}, 229 * {@link #ACTION_RESULT_ERROR_BUSY}, 230 * {@link #ACTION_RESULT_ERROR_NO_ANSWER}, 231 * {@link #ACTION_RESULT_ERROR_DELAYED}, 232 * {@link #ACTION_RESULT_ERROR_BLACKLISTED}, 233 * {@link #ACTION_RESULT_ERROR_CME}</p> 234 */ 235 public static final String EXTRA_RESULT_CODE = 236 "android.bluetooth.headsetclient.extra.RESULT_CODE"; 237 238 /** 239 * Extra for ACTION_RESULT intent that shows the extended result code of 240 * last issued action. 241 * <p>Value: <code>Integer</code> - error code.</p> 242 */ 243 public static final String EXTRA_CME_CODE = 244 "android.bluetooth.headsetclient.extra.CME_CODE"; 245 246 /* Extras for AG_FEATURES, extras type is boolean */ 247 // TODO verify if all of those are actually useful 248 /** 249 * AG feature: three way calling. 250 */ 251 public static final String EXTRA_AG_FEATURE_3WAY_CALLING = 252 "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_3WAY_CALLING"; 253 /** 254 * AG feature: voice recognition. 255 */ 256 public static final String EXTRA_AG_FEATURE_VOICE_RECOGNITION = 257 "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_VOICE_RECOGNITION"; 258 /** 259 * AG feature: fetching phone number for voice tagging procedure. 260 */ 261 public static final String EXTRA_AG_FEATURE_ATTACH_NUMBER_TO_VT = 262 "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_ATTACH_NUMBER_TO_VT"; 263 /** 264 * AG feature: ability to reject incoming call. 265 */ 266 public static final String EXTRA_AG_FEATURE_REJECT_CALL = 267 "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_REJECT_CALL"; 268 /** 269 * AG feature: enhanced call handling (terminate specific call, private consultation). 270 */ 271 public static final String EXTRA_AG_FEATURE_ECC = 272 "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_ECC"; 273 /** 274 * AG feature: response and hold. 275 */ 276 public static final String EXTRA_AG_FEATURE_RESPONSE_AND_HOLD = 277 "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_RESPONSE_AND_HOLD"; 278 /** 279 * AG call handling feature: accept held or waiting call in three way calling scenarios. 280 */ 281 public static final String EXTRA_AG_FEATURE_ACCEPT_HELD_OR_WAITING_CALL = 282 "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_ACCEPT_HELD_OR_WAITING_CALL"; 283 /** 284 * AG call handling feature: release held or waiting call in three way calling scenarios. 285 */ 286 public static final String EXTRA_AG_FEATURE_RELEASE_HELD_OR_WAITING_CALL = 287 "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_RELEASE_HELD_OR_WAITING_CALL"; 288 /** 289 * AG call handling feature: release active call and accept held or waiting call in three way 290 * calling scenarios. 291 */ 292 public static final String EXTRA_AG_FEATURE_RELEASE_AND_ACCEPT = 293 "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_RELEASE_AND_ACCEPT"; 294 /** 295 * AG call handling feature: merge two calls, held and active - multi party conference mode. 296 */ 297 public static final String EXTRA_AG_FEATURE_MERGE = 298 "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_MERGE"; 299 /** 300 * AG call handling feature: merge calls and disconnect from multi party 301 * conversation leaving peers connected to each other. 302 * Note that this feature needs to be supported by mobile network operator 303 * as it requires connection and billing transfer. 304 */ 305 public static final String EXTRA_AG_FEATURE_MERGE_AND_DETACH = 306 "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_MERGE_AND_DETACH"; 307 308 /* Action result codes */ 309 public static final int ACTION_RESULT_OK = 0; 310 public static final int ACTION_RESULT_ERROR = 1; 311 public static final int ACTION_RESULT_ERROR_NO_CARRIER = 2; 312 public static final int ACTION_RESULT_ERROR_BUSY = 3; 313 public static final int ACTION_RESULT_ERROR_NO_ANSWER = 4; 314 public static final int ACTION_RESULT_ERROR_DELAYED = 5; 315 public static final int ACTION_RESULT_ERROR_BLACKLISTED = 6; 316 public static final int ACTION_RESULT_ERROR_CME = 7; 317 318 /* Detailed CME error codes */ 319 public static final int CME_PHONE_FAILURE = 0; 320 public static final int CME_NO_CONNECTION_TO_PHONE = 1; 321 public static final int CME_OPERATION_NOT_ALLOWED = 3; 322 public static final int CME_OPERATION_NOT_SUPPORTED = 4; 323 public static final int CME_PHSIM_PIN_REQUIRED = 5; 324 public static final int CME_PHFSIM_PIN_REQUIRED = 6; 325 public static final int CME_PHFSIM_PUK_REQUIRED = 7; 326 public static final int CME_SIM_NOT_INSERTED = 10; 327 public static final int CME_SIM_PIN_REQUIRED = 11; 328 public static final int CME_SIM_PUK_REQUIRED = 12; 329 public static final int CME_SIM_FAILURE = 13; 330 public static final int CME_SIM_BUSY = 14; 331 public static final int CME_SIM_WRONG = 15; 332 public static final int CME_INCORRECT_PASSWORD = 16; 333 public static final int CME_SIM_PIN2_REQUIRED = 17; 334 public static final int CME_SIM_PUK2_REQUIRED = 18; 335 public static final int CME_MEMORY_FULL = 20; 336 public static final int CME_INVALID_INDEX = 21; 337 public static final int CME_NOT_FOUND = 22; 338 public static final int CME_MEMORY_FAILURE = 23; 339 public static final int CME_TEXT_STRING_TOO_LONG = 24; 340 public static final int CME_INVALID_CHARACTER_IN_TEXT_STRING = 25; 341 public static final int CME_DIAL_STRING_TOO_LONG = 26; 342 public static final int CME_INVALID_CHARACTER_IN_DIAL_STRING = 27; 343 public static final int CME_NO_NETWORK_SERVICE = 30; 344 public static final int CME_NETWORK_TIMEOUT = 31; 345 public static final int CME_EMERGENCY_SERVICE_ONLY = 32; 346 public static final int CME_NO_SIMULTANOUS_VOIP_CS_CALLS = 33; 347 public static final int CME_NOT_SUPPORTED_FOR_VOIP = 34; 348 public static final int CME_SIP_RESPONSE_CODE = 35; 349 public static final int CME_NETWORK_PERSONALIZATION_PIN_REQUIRED = 40; 350 public static final int CME_NETWORK_PERSONALIZATION_PUK_REQUIRED = 41; 351 public static final int CME_NETWORK_SUBSET_PERSONALIZATION_PIN_REQUIRED = 42; 352 public static final int CME_NETWORK_SUBSET_PERSONALIZATION_PUK_REQUIRED = 43; 353 public static final int CME_SERVICE_PROVIDER_PERSONALIZATION_PIN_REQUIRED = 44; 354 public static final int CME_SERVICE_PROVIDER_PERSONALIZATION_PUK_REQUIRED = 45; 355 public static final int CME_CORPORATE_PERSONALIZATION_PIN_REQUIRED = 46; 356 public static final int CME_CORPORATE_PERSONALIZATION_PUK_REQUIRED = 47; 357 public static final int CME_HIDDEN_KEY_REQUIRED = 48; 358 public static final int CME_EAP_NOT_SUPPORTED = 49; 359 public static final int CME_INCORRECT_PARAMETERS = 50; 360 361 /* Action policy for other calls when accepting call */ 362 public static final int CALL_ACCEPT_NONE = 0; 363 public static final int CALL_ACCEPT_HOLD = 1; 364 public static final int CALL_ACCEPT_TERMINATE = 2; 365 366 private BluetoothAdapter mAdapter; 367 private final BluetoothProfileConnector<IBluetoothHeadsetClient> mProfileConnector = 368 new BluetoothProfileConnector(this, BluetoothProfile.HEADSET_CLIENT, 369 "BluetoothHeadsetClient", IBluetoothHeadsetClient.class.getName()) { 370 @Override 371 public IBluetoothHeadsetClient getServiceInterface(IBinder service) { 372 return IBluetoothHeadsetClient.Stub.asInterface(Binder.allowBlocking(service)); 373 } 374 }; 375 376 /** 377 * Create a BluetoothHeadsetClient proxy object. 378 */ 379 /*package*/ BluetoothHeadsetClient(Context context, ServiceListener listener) { 380 mAdapter = BluetoothAdapter.getDefaultAdapter(); 381 mProfileConnector.connect(context, listener); 382 } 383 384 /** 385 * Close the connection to the backing service. 386 * Other public functions of BluetoothHeadsetClient will return default error 387 * results once close() has been called. Multiple invocations of close() 388 * are ok. 389 */ 390 /*package*/ void close() { 391 if (VDBG) log("close()"); 392 mProfileConnector.disconnect(); 393 } 394 395 private IBluetoothHeadsetClient getService() { 396 return mProfileConnector.getService(); 397 } 398 399 /** 400 * Connects to remote device. 401 * 402 * Currently, the system supports only 1 connection. So, in case of the 403 * second connection, this implementation will disconnect already connected 404 * device automatically and will process the new one. 405 * 406 * @param device a remote device we want connect to 407 * @return <code>true</code> if command has been issued successfully; <code>false</code> 408 * otherwise; upon completion HFP sends {@link #ACTION_CONNECTION_STATE_CHANGED} intent. 409 */ 410 @UnsupportedAppUsage 411 public boolean connect(BluetoothDevice device) { 412 if (DBG) log("connect(" + device + ")"); 413 final IBluetoothHeadsetClient service = 414 getService(); 415 if (service != null && isEnabled() && isValidDevice(device)) { 416 try { 417 return service.connect(device); 418 } catch (RemoteException e) { 419 Log.e(TAG, Log.getStackTraceString(new Throwable())); 420 return false; 421 } 422 } 423 if (service == null) Log.w(TAG, "Proxy not attached to service"); 424 return false; 425 } 426 427 /** 428 * Disconnects remote device 429 * 430 * @param device a remote device we want disconnect 431 * @return <code>true</code> if command has been issued successfully; <code>false</code> 432 * otherwise; upon completion HFP sends {@link #ACTION_CONNECTION_STATE_CHANGED} intent. 433 */ 434 @UnsupportedAppUsage 435 public boolean disconnect(BluetoothDevice device) { 436 if (DBG) log("disconnect(" + device + ")"); 437 final IBluetoothHeadsetClient service = 438 getService(); 439 if (service != null && isEnabled() && isValidDevice(device)) { 440 try { 441 return service.disconnect(device); 442 } catch (RemoteException e) { 443 Log.e(TAG, Log.getStackTraceString(new Throwable())); 444 return false; 445 } 446 } 447 if (service == null) Log.w(TAG, "Proxy not attached to service"); 448 return false; 449 } 450 451 /** 452 * Return the list of connected remote devices 453 * 454 * @return list of connected devices; empty list if nothing is connected. 455 */ 456 @Override 457 public List<BluetoothDevice> getConnectedDevices() { 458 if (VDBG) log("getConnectedDevices()"); 459 final IBluetoothHeadsetClient service = 460 getService(); 461 if (service != null && isEnabled()) { 462 try { 463 return service.getConnectedDevices(); 464 } catch (RemoteException e) { 465 Log.e(TAG, Log.getStackTraceString(new Throwable())); 466 return new ArrayList<BluetoothDevice>(); 467 } 468 } 469 if (service == null) Log.w(TAG, "Proxy not attached to service"); 470 return new ArrayList<BluetoothDevice>(); 471 } 472 473 /** 474 * Returns list of remote devices in a particular state 475 * 476 * @param states collection of states 477 * @return list of devices that state matches the states listed in <code>states</code>; empty 478 * list if nothing matches the <code>states</code> 479 */ 480 @Override 481 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 482 if (VDBG) log("getDevicesMatchingStates()"); 483 final IBluetoothHeadsetClient service = 484 getService(); 485 if (service != null && isEnabled()) { 486 try { 487 return service.getDevicesMatchingConnectionStates(states); 488 } catch (RemoteException e) { 489 Log.e(TAG, Log.getStackTraceString(new Throwable())); 490 return new ArrayList<BluetoothDevice>(); 491 } 492 } 493 if (service == null) Log.w(TAG, "Proxy not attached to service"); 494 return new ArrayList<BluetoothDevice>(); 495 } 496 497 /** 498 * Returns state of the <code>device</code> 499 * 500 * @param device a remote device 501 * @return the state of connection of the device 502 */ 503 @Override 504 public int getConnectionState(BluetoothDevice device) { 505 if (VDBG) log("getConnectionState(" + device + ")"); 506 final IBluetoothHeadsetClient service = 507 getService(); 508 if (service != null && isEnabled() && isValidDevice(device)) { 509 try { 510 return service.getConnectionState(device); 511 } catch (RemoteException e) { 512 Log.e(TAG, Log.getStackTraceString(new Throwable())); 513 return BluetoothProfile.STATE_DISCONNECTED; 514 } 515 } 516 if (service == null) Log.w(TAG, "Proxy not attached to service"); 517 return BluetoothProfile.STATE_DISCONNECTED; 518 } 519 520 /** 521 * Set priority of the profile 522 * 523 * The device should already be paired. 524 */ 525 public boolean setPriority(BluetoothDevice device, int priority) { 526 if (DBG) log("setPriority(" + device + ", " + priority + ")"); 527 final IBluetoothHeadsetClient service = 528 getService(); 529 if (service != null && isEnabled() && isValidDevice(device)) { 530 if (priority != BluetoothProfile.PRIORITY_OFF 531 && priority != BluetoothProfile.PRIORITY_ON) { 532 return false; 533 } 534 try { 535 return service.setPriority(device, priority); 536 } catch (RemoteException e) { 537 Log.e(TAG, Log.getStackTraceString(new Throwable())); 538 return false; 539 } 540 } 541 if (service == null) Log.w(TAG, "Proxy not attached to service"); 542 return false; 543 } 544 545 /** 546 * Get the priority of the profile. 547 */ 548 public int getPriority(BluetoothDevice device) { 549 if (VDBG) log("getPriority(" + device + ")"); 550 final IBluetoothHeadsetClient service = 551 getService(); 552 if (service != null && isEnabled() && isValidDevice(device)) { 553 try { 554 return service.getPriority(device); 555 } catch (RemoteException e) { 556 Log.e(TAG, Log.getStackTraceString(new Throwable())); 557 return PRIORITY_OFF; 558 } 559 } 560 if (service == null) Log.w(TAG, "Proxy not attached to service"); 561 return PRIORITY_OFF; 562 } 563 564 /** 565 * Starts voice recognition. 566 * 567 * @param device remote device 568 * @return <code>true</code> if command has been issued successfully; <code>false</code> 569 * otherwise; upon completion HFP sends {@link #ACTION_AG_EVENT} intent. 570 * 571 * <p>Feature required for successful execution is being reported by: {@link 572 * #EXTRA_AG_FEATURE_VOICE_RECOGNITION}. This method invocation will fail silently when feature 573 * is not supported.</p> 574 */ 575 public boolean startVoiceRecognition(BluetoothDevice device) { 576 if (DBG) log("startVoiceRecognition()"); 577 final IBluetoothHeadsetClient service = 578 getService(); 579 if (service != null && isEnabled() && isValidDevice(device)) { 580 try { 581 return service.startVoiceRecognition(device); 582 } catch (RemoteException e) { 583 Log.e(TAG, Log.getStackTraceString(new Throwable())); 584 } 585 } 586 if (service == null) Log.w(TAG, "Proxy not attached to service"); 587 return false; 588 } 589 590 /** 591 * Stops voice recognition. 592 * 593 * @param device remote device 594 * @return <code>true</code> if command has been issued successfully; <code>false</code> 595 * otherwise; upon completion HFP sends {@link #ACTION_AG_EVENT} intent. 596 * 597 * <p>Feature required for successful execution is being reported by: {@link 598 * #EXTRA_AG_FEATURE_VOICE_RECOGNITION}. This method invocation will fail silently when feature 599 * is not supported.</p> 600 */ 601 public boolean stopVoiceRecognition(BluetoothDevice device) { 602 if (DBG) log("stopVoiceRecognition()"); 603 final IBluetoothHeadsetClient service = 604 getService(); 605 if (service != null && isEnabled() && isValidDevice(device)) { 606 try { 607 return service.stopVoiceRecognition(device); 608 } catch (RemoteException e) { 609 Log.e(TAG, Log.getStackTraceString(new Throwable())); 610 } 611 } 612 if (service == null) Log.w(TAG, "Proxy not attached to service"); 613 return false; 614 } 615 616 /** 617 * Returns list of all calls in any state. 618 * 619 * @param device remote device 620 * @return list of calls; empty list if none call exists 621 */ 622 public List<BluetoothHeadsetClientCall> getCurrentCalls(BluetoothDevice device) { 623 if (DBG) log("getCurrentCalls()"); 624 final IBluetoothHeadsetClient service = 625 getService(); 626 if (service != null && isEnabled() && isValidDevice(device)) { 627 try { 628 return service.getCurrentCalls(device); 629 } catch (RemoteException e) { 630 Log.e(TAG, Log.getStackTraceString(new Throwable())); 631 } 632 } 633 if (service == null) Log.w(TAG, "Proxy not attached to service"); 634 return null; 635 } 636 637 /** 638 * Returns list of current values of AG indicators. 639 * 640 * @param device remote device 641 * @return bundle of AG indicators; null if device is not in CONNECTED state 642 */ 643 public Bundle getCurrentAgEvents(BluetoothDevice device) { 644 if (DBG) log("getCurrentCalls()"); 645 final IBluetoothHeadsetClient service = 646 getService(); 647 if (service != null && isEnabled() && isValidDevice(device)) { 648 try { 649 return service.getCurrentAgEvents(device); 650 } catch (RemoteException e) { 651 Log.e(TAG, Log.getStackTraceString(new Throwable())); 652 } 653 } 654 if (service == null) Log.w(TAG, "Proxy not attached to service"); 655 return null; 656 } 657 658 /** 659 * Accepts a call 660 * 661 * @param device remote device 662 * @param flag action policy while accepting a call. Possible values {@link #CALL_ACCEPT_NONE}, 663 * {@link #CALL_ACCEPT_HOLD}, {@link #CALL_ACCEPT_TERMINATE} 664 * @return <code>true</code> if command has been issued successfully; <code>false</code> 665 * otherwise; upon completion HFP sends {@link #ACTION_CALL_CHANGED} intent. 666 */ 667 @UnsupportedAppUsage 668 public boolean acceptCall(BluetoothDevice device, int flag) { 669 if (DBG) log("acceptCall()"); 670 final IBluetoothHeadsetClient service = 671 getService(); 672 if (service != null && isEnabled() && isValidDevice(device)) { 673 try { 674 return service.acceptCall(device, flag); 675 } catch (RemoteException e) { 676 Log.e(TAG, Log.getStackTraceString(new Throwable())); 677 } 678 } 679 if (service == null) Log.w(TAG, "Proxy not attached to service"); 680 return false; 681 } 682 683 /** 684 * Holds a call. 685 * 686 * @param device remote device 687 * @return <code>true</code> if command has been issued successfully; <code>false</code> 688 * otherwise; upon completion HFP sends {@link #ACTION_CALL_CHANGED} intent. 689 */ 690 public boolean holdCall(BluetoothDevice device) { 691 if (DBG) log("holdCall()"); 692 final IBluetoothHeadsetClient service = 693 getService(); 694 if (service != null && isEnabled() && isValidDevice(device)) { 695 try { 696 return service.holdCall(device); 697 } catch (RemoteException e) { 698 Log.e(TAG, Log.getStackTraceString(new Throwable())); 699 } 700 } 701 if (service == null) Log.w(TAG, "Proxy not attached to service"); 702 return false; 703 } 704 705 /** 706 * Rejects a call. 707 * 708 * @param device remote device 709 * @return <code>true</code> if command has been issued successfully; <code>false</code> 710 * otherwise; upon completion HFP sends {@link #ACTION_CALL_CHANGED} intent. 711 * 712 * <p>Feature required for successful execution is being reported by: {@link 713 * #EXTRA_AG_FEATURE_REJECT_CALL}. This method invocation will fail silently when feature is not 714 * supported.</p> 715 */ 716 @UnsupportedAppUsage 717 public boolean rejectCall(BluetoothDevice device) { 718 if (DBG) log("rejectCall()"); 719 final IBluetoothHeadsetClient service = 720 getService(); 721 if (service != null && isEnabled() && isValidDevice(device)) { 722 try { 723 return service.rejectCall(device); 724 } catch (RemoteException e) { 725 Log.e(TAG, Log.getStackTraceString(new Throwable())); 726 } 727 } 728 if (service == null) Log.w(TAG, "Proxy not attached to service"); 729 return false; 730 } 731 732 /** 733 * Terminates a specified call. 734 * 735 * Works only when Extended Call Control is supported by Audio Gateway. 736 * 737 * @param device remote device 738 * @param call Handle of call obtained in {@link #dial(BluetoothDevice, String)} or obtained via 739 * {@link #ACTION_CALL_CHANGED}. {@code call} may be null in which case we will hangup all active 740 * calls. 741 * @return <code>true</code> if command has been issued successfully; <code>false</code> 742 * otherwise; upon completion HFP sends {@link #ACTION_CALL_CHANGED} intent. 743 * 744 * <p>Feature required for successful execution is being reported by: {@link 745 * #EXTRA_AG_FEATURE_ECC}. This method invocation will fail silently when feature is not 746 * supported.</p> 747 */ 748 public boolean terminateCall(BluetoothDevice device, BluetoothHeadsetClientCall call) { 749 if (DBG) log("terminateCall()"); 750 final IBluetoothHeadsetClient service = 751 getService(); 752 if (service != null && isEnabled() && isValidDevice(device)) { 753 try { 754 return service.terminateCall(device, call); 755 } catch (RemoteException e) { 756 Log.e(TAG, Log.getStackTraceString(new Throwable())); 757 } 758 } 759 if (service == null) Log.w(TAG, "Proxy not attached to service"); 760 return false; 761 } 762 763 /** 764 * Enters private mode with a specified call. 765 * 766 * Works only when Extended Call Control is supported by Audio Gateway. 767 * 768 * @param device remote device 769 * @param index index of the call to connect in private mode 770 * @return <code>true</code> if command has been issued successfully; <code>false</code> 771 * otherwise; upon completion HFP sends {@link #ACTION_CALL_CHANGED} intent. 772 * 773 * <p>Feature required for successful execution is being reported by: {@link 774 * #EXTRA_AG_FEATURE_ECC}. This method invocation will fail silently when feature is not 775 * supported.</p> 776 */ 777 public boolean enterPrivateMode(BluetoothDevice device, int index) { 778 if (DBG) log("enterPrivateMode()"); 779 final IBluetoothHeadsetClient service = 780 getService(); 781 if (service != null && isEnabled() && isValidDevice(device)) { 782 try { 783 return service.enterPrivateMode(device, index); 784 } catch (RemoteException e) { 785 Log.e(TAG, Log.getStackTraceString(new Throwable())); 786 } 787 } 788 if (service == null) Log.w(TAG, "Proxy not attached to service"); 789 return false; 790 } 791 792 /** 793 * Performs explicit call transfer. 794 * 795 * That means connect other calls and disconnect. 796 * 797 * @param device remote device 798 * @return <code>true</code> if command has been issued successfully; <code>false</code> 799 * otherwise; upon completion HFP sends {@link #ACTION_CALL_CHANGED} intent. 800 * 801 * <p>Feature required for successful execution is being reported by: {@link 802 * #EXTRA_AG_FEATURE_MERGE_AND_DETACH}. This method invocation will fail silently when feature 803 * is not supported.</p> 804 */ 805 public boolean explicitCallTransfer(BluetoothDevice device) { 806 if (DBG) log("explicitCallTransfer()"); 807 final IBluetoothHeadsetClient service = 808 getService(); 809 if (service != null && isEnabled() && isValidDevice(device)) { 810 try { 811 return service.explicitCallTransfer(device); 812 } catch (RemoteException e) { 813 Log.e(TAG, Log.getStackTraceString(new Throwable())); 814 } 815 } 816 if (service == null) Log.w(TAG, "Proxy not attached to service"); 817 return false; 818 } 819 820 /** 821 * Places a call with specified number. 822 * 823 * @param device remote device 824 * @param number valid phone number 825 * @return <code>{@link BluetoothHeadsetClientCall} call</code> if command has been issued 826 * successfully; <code>{@link null}</code> otherwise; upon completion HFP sends {@link 827 * #ACTION_CALL_CHANGED} intent in case of success; {@link #ACTION_RESULT} is sent otherwise; 828 */ 829 public BluetoothHeadsetClientCall dial(BluetoothDevice device, String number) { 830 if (DBG) log("dial()"); 831 final IBluetoothHeadsetClient service = 832 getService(); 833 if (service != null && isEnabled() && isValidDevice(device)) { 834 try { 835 return service.dial(device, number); 836 } catch (RemoteException e) { 837 Log.e(TAG, Log.getStackTraceString(new Throwable())); 838 } 839 } 840 if (service == null) Log.w(TAG, "Proxy not attached to service"); 841 return null; 842 } 843 844 /** 845 * Sends DTMF code. 846 * 847 * Possible code values : 0,1,2,3,4,5,6,7,8,9,A,B,C,D,*,# 848 * 849 * @param device remote device 850 * @param code ASCII code 851 * @return <code>true</code> if command has been issued successfully; <code>false</code> 852 * otherwise; upon completion HFP sends {@link #ACTION_RESULT} intent; 853 */ 854 public boolean sendDTMF(BluetoothDevice device, byte code) { 855 if (DBG) log("sendDTMF()"); 856 final IBluetoothHeadsetClient service = 857 getService(); 858 if (service != null && isEnabled() && isValidDevice(device)) { 859 try { 860 return service.sendDTMF(device, code); 861 } catch (RemoteException e) { 862 Log.e(TAG, Log.getStackTraceString(new Throwable())); 863 } 864 } 865 if (service == null) Log.w(TAG, "Proxy not attached to service"); 866 return false; 867 } 868 869 /** 870 * Get a number corresponding to last voice tag recorded on AG. 871 * 872 * @param device remote device 873 * @return <code>true</code> if command has been issued successfully; <code>false</code> 874 * otherwise; upon completion HFP sends {@link #ACTION_LAST_VTAG} or {@link #ACTION_RESULT} 875 * intent; 876 * 877 * <p>Feature required for successful execution is being reported by: {@link 878 * #EXTRA_AG_FEATURE_ATTACH_NUMBER_TO_VT}. This method invocation will fail silently when 879 * feature is not supported.</p> 880 */ 881 public boolean getLastVoiceTagNumber(BluetoothDevice device) { 882 if (DBG) log("getLastVoiceTagNumber()"); 883 final IBluetoothHeadsetClient service = 884 getService(); 885 if (service != null && isEnabled() && isValidDevice(device)) { 886 try { 887 return service.getLastVoiceTagNumber(device); 888 } catch (RemoteException e) { 889 Log.e(TAG, Log.getStackTraceString(new Throwable())); 890 } 891 } 892 if (service == null) Log.w(TAG, "Proxy not attached to service"); 893 return false; 894 } 895 896 /** 897 * Returns current audio state of Audio Gateway. 898 * 899 * Note: This is an internal function and shouldn't be exposed 900 */ 901 @UnsupportedAppUsage 902 public int getAudioState(BluetoothDevice device) { 903 if (VDBG) log("getAudioState"); 904 final IBluetoothHeadsetClient service = 905 getService(); 906 if (service != null && isEnabled()) { 907 try { 908 return service.getAudioState(device); 909 } catch (RemoteException e) { 910 Log.e(TAG, e.toString()); 911 } 912 } else { 913 Log.w(TAG, "Proxy not attached to service"); 914 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); 915 } 916 return BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED; 917 } 918 919 /** 920 * Sets whether audio routing is allowed. 921 * 922 * @param device remote device 923 * @param allowed if routing is allowed to the device Note: This is an internal function and 924 * shouldn't be exposed 925 */ 926 public void setAudioRouteAllowed(BluetoothDevice device, boolean allowed) { 927 if (VDBG) log("setAudioRouteAllowed"); 928 final IBluetoothHeadsetClient service = 929 getService(); 930 if (service != null && isEnabled()) { 931 try { 932 service.setAudioRouteAllowed(device, allowed); 933 } catch (RemoteException e) { 934 Log.e(TAG, e.toString()); 935 } 936 } else { 937 Log.w(TAG, "Proxy not attached to service"); 938 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); 939 } 940 } 941 942 /** 943 * Returns whether audio routing is allowed. 944 * 945 * @param device remote device 946 * @return whether the command succeeded Note: This is an internal function and shouldn't be 947 * exposed 948 */ 949 public boolean getAudioRouteAllowed(BluetoothDevice device) { 950 if (VDBG) log("getAudioRouteAllowed"); 951 final IBluetoothHeadsetClient service = 952 getService(); 953 if (service != null && isEnabled()) { 954 try { 955 return service.getAudioRouteAllowed(device); 956 } catch (RemoteException e) { 957 Log.e(TAG, e.toString()); 958 } 959 } else { 960 Log.w(TAG, "Proxy not attached to service"); 961 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); 962 } 963 return false; 964 } 965 966 /** 967 * Initiates a connection of audio channel. 968 * 969 * It setup SCO channel with remote connected Handsfree AG device. 970 * 971 * @param device remote device 972 * @return <code>true</code> if command has been issued successfully; <code>false</code> 973 * otherwise; upon completion HFP sends {@link #ACTION_AUDIO_STATE_CHANGED} intent; 974 */ 975 public boolean connectAudio(BluetoothDevice device) { 976 final IBluetoothHeadsetClient service = 977 getService(); 978 if (service != null && isEnabled()) { 979 try { 980 return service.connectAudio(device); 981 } catch (RemoteException e) { 982 Log.e(TAG, e.toString()); 983 } 984 } else { 985 Log.w(TAG, "Proxy not attached to service"); 986 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); 987 } 988 return false; 989 } 990 991 /** 992 * Disconnects audio channel. 993 * 994 * It tears down the SCO channel from remote AG device. 995 * 996 * @param device remote device 997 * @return <code>true</code> if command has been issued successfully; <code>false</code> 998 * otherwise; upon completion HFP sends {@link #ACTION_AUDIO_STATE_CHANGED} intent; 999 */ 1000 public boolean disconnectAudio(BluetoothDevice device) { 1001 final IBluetoothHeadsetClient service = 1002 getService(); 1003 if (service != null && isEnabled()) { 1004 try { 1005 return service.disconnectAudio(device); 1006 } catch (RemoteException e) { 1007 Log.e(TAG, e.toString()); 1008 } 1009 } else { 1010 Log.w(TAG, "Proxy not attached to service"); 1011 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); 1012 } 1013 return false; 1014 } 1015 1016 /** 1017 * Get Audio Gateway features 1018 * 1019 * @param device remote device 1020 * @return bundle of AG features; null if no service or AG not connected 1021 */ 1022 public Bundle getCurrentAgFeatures(BluetoothDevice device) { 1023 final IBluetoothHeadsetClient service = 1024 getService(); 1025 if (service != null && isEnabled()) { 1026 try { 1027 return service.getCurrentAgFeatures(device); 1028 } catch (RemoteException e) { 1029 Log.e(TAG, e.toString()); 1030 } 1031 } else { 1032 Log.w(TAG, "Proxy not attached to service"); 1033 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); 1034 } 1035 return null; 1036 } 1037 1038 private boolean isEnabled() { 1039 return mAdapter.getState() == BluetoothAdapter.STATE_ON; 1040 } 1041 1042 private static boolean isValidDevice(BluetoothDevice device) { 1043 return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress()); 1044 } 1045 1046 private static void log(String msg) { 1047 Log.d(TAG, msg); 1048 } 1049 } 1050