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