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