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 package android.bluetooth; 17 18 import android.annotation.IntRange; 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.annotation.RequiresPermission; 22 import android.annotation.SdkConstant; 23 import android.annotation.SdkConstant.SdkConstantType; 24 import android.annotation.SuppressLint; 25 import android.annotation.SystemApi; 26 import android.bluetooth.annotations.RequiresBluetoothConnectPermission; 27 import android.bluetooth.annotations.RequiresLegacyBluetoothPermission; 28 import android.compat.annotation.UnsupportedAppUsage; 29 import android.content.AttributionSource; 30 import android.content.Context; 31 import android.os.Build; 32 import android.os.Bundle; 33 import android.os.IBinder; 34 import android.os.Parcel; 35 import android.os.Parcelable; 36 import android.os.RemoteException; 37 import android.util.CloseGuard; 38 import android.util.Log; 39 40 import java.util.Collections; 41 import java.util.List; 42 43 /** 44 * This class provides the System APIs to interact with the Hands-Free Client profile. 45 * 46 * <p>BluetoothHeadsetClient is a proxy object for controlling the Bluetooth HFP Client Service via 47 * IPC. Use {@link BluetoothAdapter#getProfileProxy} to get the BluetoothHeadsetClient proxy object. 48 * 49 * @hide 50 */ 51 @SystemApi 52 public final class BluetoothHeadsetClient implements BluetoothProfile, AutoCloseable { 53 private static final String TAG = "BluetoothHeadsetClient"; 54 private static final boolean DBG = true; 55 private static final boolean VDBG = false; 56 private final CloseGuard mCloseGuard; 57 58 /** 59 * Intent used to broadcast the change in connection state of the HFP Client profile. 60 * 61 * <p>This intent will have 3 extras: 62 * 63 * <ul> 64 * <li>{@link #EXTRA_STATE} - The current state of the profile. 65 * <li>{@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile. 66 * <li>{@link BluetoothDevice#EXTRA_DEVICE} - The remote device. 67 * </ul> 68 * 69 * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of {@link 70 * #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, {@link #STATE_CONNECTED}, {@link 71 * #STATE_DISCONNECTING}. 72 * 73 * @hide 74 */ 75 @SuppressLint("ActionValue") 76 @SystemApi 77 @RequiresBluetoothConnectPermission 78 @RequiresPermission( 79 allOf = { 80 android.Manifest.permission.BLUETOOTH_CONNECT, 81 android.Manifest.permission.BLUETOOTH_PRIVILEGED, 82 }) 83 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 84 public static final String ACTION_CONNECTION_STATE_CHANGED = 85 "android.bluetooth.headsetclient.profile.action.CONNECTION_STATE_CHANGED"; 86 87 /** 88 * Intent sent whenever audio state changes. 89 * 90 * <p>It includes two mandatory extras: {@link BluetoothProfile#EXTRA_STATE}, {@link 91 * BluetoothProfile#EXTRA_PREVIOUS_STATE}, with possible values: {@link 92 * #STATE_AUDIO_CONNECTING}, {@link #STATE_AUDIO_CONNECTED}, {@link #STATE_AUDIO_DISCONNECTED} 93 * 94 * <p>When <code>EXTRA_STATE</code> is set to </code>STATE_AUDIO_CONNECTED</code>, it also 95 * includes {@link #EXTRA_AUDIO_WBS} indicating wide band speech support. 96 * 97 * @hide 98 */ 99 @RequiresBluetoothConnectPermission 100 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) 101 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 102 @SuppressLint("ActionValue") 103 public static final String ACTION_AUDIO_STATE_CHANGED = 104 "android.bluetooth.headsetclient.profile.action.AUDIO_STATE_CHANGED"; 105 106 /** 107 * Intent sending updates of the Audio Gateway state. Each extra is being sent only when value 108 * it represents has been changed recently on AG. 109 * 110 * <p>It can contain one or more of the following extras: {@link #EXTRA_NETWORK_STATUS}, {@link 111 * #EXTRA_NETWORK_SIGNAL_STRENGTH}, {@link #EXTRA_NETWORK_ROAMING}, {@link 112 * #EXTRA_BATTERY_LEVEL}, {@link #EXTRA_OPERATOR_NAME}, {@link #EXTRA_VOICE_RECOGNITION}, {@link 113 * #EXTRA_IN_BAND_RING} 114 * 115 * @hide 116 */ 117 @RequiresBluetoothConnectPermission 118 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) 119 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 120 public static final String ACTION_AG_EVENT = 121 "android.bluetooth.headsetclient.profile.action.AG_EVENT"; 122 123 /** 124 * Intent sent whenever state of a call changes. 125 * 126 * <p>It includes: {@link #EXTRA_CALL}, with value of {@link BluetoothHeadsetClientCall} 127 * instance, representing actual call state. 128 * 129 * @hide 130 */ 131 @RequiresBluetoothConnectPermission 132 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) 133 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 134 public static final String ACTION_CALL_CHANGED = 135 "android.bluetooth.headsetclient.profile.action.AG_CALL_CHANGED"; 136 137 /** 138 * Intent that notifies about the result of the last issued action. Please note that not every 139 * action results in explicit action result code being sent. Instead other notifications about 140 * new Audio Gateway state might be sent, like <code>ACTION_AG_EVENT</code> with <code> 141 * EXTRA_VOICE_RECOGNITION</code> value when for example user started voice recognition from HF 142 * unit. 143 * 144 * @hide 145 */ 146 @RequiresBluetoothConnectPermission 147 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) 148 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 149 public static final String ACTION_RESULT = 150 "android.bluetooth.headsetclient.profile.action.RESULT"; 151 152 /** 153 * Intent that notifies about vendor specific event arrival. Events not defined in HFP spec will 154 * be matched with supported vendor event list and this intent will be broadcasted upon a match. 155 * Supported vendor events are of format of "+eventCode" or "+eventCode=xxxx" or 156 * "+eventCode:=xxxx". Vendor event can be a response to a vendor specific command or 157 * unsolicited. 158 * 159 * @hide 160 */ 161 @RequiresBluetoothConnectPermission 162 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) 163 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 164 public static final String ACTION_VENDOR_SPECIFIC_HEADSETCLIENT_EVENT = 165 "android.bluetooth.headsetclient.profile.action.VENDOR_SPECIFIC_EVENT"; 166 167 /** 168 * Intent that notifies about the number attached to the last voice tag recorded on AG. 169 * 170 * <p>It contains: {@link #EXTRA_NUMBER}, with a <code>String</code> value representing phone 171 * number. 172 * 173 * @hide 174 */ 175 @RequiresBluetoothConnectPermission 176 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) 177 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 178 public static final String ACTION_LAST_VTAG = 179 "android.bluetooth.headsetclient.profile.action.LAST_VTAG"; 180 181 /** @hide */ 182 public static final int STATE_AUDIO_DISCONNECTED = 0; 183 184 /** @hide */ 185 public static final int STATE_AUDIO_CONNECTING = 1; 186 187 /** @hide */ 188 public static final int STATE_AUDIO_CONNECTED = 2; 189 190 /** 191 * Extra with information if connected audio is WBS. 192 * 193 * <p>Possible values: <code>true</code>, <code>false</code>. 194 * 195 * @hide 196 */ 197 public static final String EXTRA_AUDIO_WBS = "android.bluetooth.headsetclient.extra.AUDIO_WBS"; 198 199 /** 200 * Extra for AG_EVENT indicates network status. 201 * 202 * <p>Value: 0 - network unavailable, 1 - network available 203 * 204 * @hide 205 */ 206 public static final String EXTRA_NETWORK_STATUS = 207 "android.bluetooth.headsetclient.extra.NETWORK_STATUS"; 208 209 /** 210 * Extra for AG_EVENT intent indicates network signal strength. 211 * 212 * <p>Value: <code>Integer</code> representing signal strength. 213 * 214 * @hide 215 */ 216 public static final String EXTRA_NETWORK_SIGNAL_STRENGTH = 217 "android.bluetooth.headsetclient.extra.NETWORK_SIGNAL_STRENGTH"; 218 219 /** 220 * Extra for AG_EVENT intent indicates roaming state. 221 * 222 * <p>Value: 0 - no roaming 1 - active roaming 223 * 224 * @hide 225 */ 226 public static final String EXTRA_NETWORK_ROAMING = 227 "android.bluetooth.headsetclient.extra.NETWORK_ROAMING"; 228 229 /** 230 * Extra for AG_EVENT intent indicates the battery level. 231 * 232 * <p>Value: <code>Integer</code> representing signal strength. 233 * 234 * @hide 235 */ 236 public static final String EXTRA_BATTERY_LEVEL = 237 "android.bluetooth.headsetclient.extra.BATTERY_LEVEL"; 238 239 /** 240 * Extra for AG_EVENT intent indicates operator name. 241 * 242 * <p>Value: <code>String</code> representing operator name. 243 * 244 * @hide 245 */ 246 public static final String EXTRA_OPERATOR_NAME = 247 "android.bluetooth.headsetclient.extra.OPERATOR_NAME"; 248 249 /** 250 * Extra for AG_EVENT intent indicates voice recognition state. 251 * 252 * <p>Value: 0 - voice recognition stopped, 1 - voice recognition started. 253 * 254 * @hide 255 */ 256 public static final String EXTRA_VOICE_RECOGNITION = 257 "android.bluetooth.headsetclient.extra.VOICE_RECOGNITION"; 258 259 /** 260 * Extra for AG_EVENT intent indicates in band ring state. 261 * 262 * <p>Value: 0 - in band ring tone not supported, or 1 - in band ring tone supported. 263 * 264 * @hide 265 */ 266 public static final String EXTRA_IN_BAND_RING = 267 "android.bluetooth.headsetclient.extra.IN_BAND_RING"; 268 269 /** 270 * Extra for AG_EVENT intent indicates subscriber info. 271 * 272 * <p>Value: <code>String</code> containing subscriber information. 273 * 274 * @hide 275 */ 276 public static final String EXTRA_SUBSCRIBER_INFO = 277 "android.bluetooth.headsetclient.extra.SUBSCRIBER_INFO"; 278 279 /** 280 * Extra for AG_CALL_CHANGED intent indicates the {@link BluetoothHeadsetClientCall} object that 281 * has changed. 282 * 283 * @hide 284 */ 285 public static final String EXTRA_CALL = "android.bluetooth.headsetclient.extra.CALL"; 286 287 /** 288 * Extra for ACTION_LAST_VTAG intent. 289 * 290 * <p>Value: <code>String</code> representing phone number corresponding to last voice tag 291 * recorded on AG 292 * 293 * @hide 294 */ 295 public static final String EXTRA_NUMBER = "android.bluetooth.headsetclient.extra.NUMBER"; 296 297 /** 298 * Extra for ACTION_RESULT intent that shows the result code of last issued action. 299 * 300 * <p>Possible results: {@link #ACTION_RESULT_OK}, {@link #ACTION_RESULT_ERROR}, {@link 301 * #ACTION_RESULT_ERROR_NO_CARRIER}, {@link #ACTION_RESULT_ERROR_BUSY}, {@link 302 * #ACTION_RESULT_ERROR_NO_ANSWER}, {@link #ACTION_RESULT_ERROR_DELAYED}, {@link 303 * #ACTION_RESULT_ERROR_BLACKLISTED}, {@link #ACTION_RESULT_ERROR_CME} 304 * 305 * @hide 306 */ 307 public static final String EXTRA_RESULT_CODE = 308 "android.bluetooth.headsetclient.extra.RESULT_CODE"; 309 310 /** 311 * Extra for ACTION_RESULT intent that shows the extended result code of last issued action. 312 * 313 * <p>Value: <code>Integer</code> - error code. 314 * 315 * @hide 316 */ 317 public static final String EXTRA_CME_CODE = "android.bluetooth.headsetclient.extra.CME_CODE"; 318 319 /** 320 * Extra for VENDOR_SPECIFIC_HEADSETCLIENT_EVENT intent that indicates vendor ID. 321 * 322 * @hide 323 */ 324 public static final String EXTRA_VENDOR_ID = "android.bluetooth.headsetclient.extra.VENDOR_ID"; 325 326 /** 327 * Extra for VENDOR_SPECIFIC_HEADSETCLIENT_EVENT intent that indicates vendor event code. 328 * 329 * @hide 330 */ 331 public static final String EXTRA_VENDOR_EVENT_CODE = 332 "android.bluetooth.headsetclient.extra.VENDOR_EVENT_CODE"; 333 334 /** 335 * Extra for VENDOR_SPECIFIC_HEADSETCLIENT_EVENT intent that contains full vendor event 336 * including event code and full arguments. 337 * 338 * @hide 339 */ 340 public static final String EXTRA_VENDOR_EVENT_FULL_ARGS = 341 "android.bluetooth.headsetclient.extra.VENDOR_EVENT_FULL_ARGS"; 342 343 /* Extras for AG_FEATURES, extras type is boolean */ 344 // TODO verify if all of those are actually useful 345 /** 346 * AG feature: three way calling. 347 * 348 * @hide 349 */ 350 public static final String EXTRA_AG_FEATURE_3WAY_CALLING = 351 "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_3WAY_CALLING"; 352 353 /** 354 * AG feature: voice recognition. 355 * 356 * @hide 357 */ 358 public static final String EXTRA_AG_FEATURE_VOICE_RECOGNITION = 359 "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_VOICE_RECOGNITION"; 360 361 /** 362 * AG feature: fetching phone number for voice tagging procedure. 363 * 364 * @hide 365 */ 366 public static final String EXTRA_AG_FEATURE_ATTACH_NUMBER_TO_VT = 367 "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_ATTACH_NUMBER_TO_VT"; 368 369 /** 370 * AG feature: ability to reject incoming call. 371 * 372 * @hide 373 */ 374 public static final String EXTRA_AG_FEATURE_REJECT_CALL = 375 "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_REJECT_CALL"; 376 377 /** 378 * AG feature: enhanced call handling (terminate specific call, private consultation). 379 * 380 * @hide 381 */ 382 public static final String EXTRA_AG_FEATURE_ECC = 383 "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_ECC"; 384 385 /** 386 * AG feature: response and hold. 387 * 388 * @hide 389 */ 390 public static final String EXTRA_AG_FEATURE_RESPONSE_AND_HOLD = 391 "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_RESPONSE_AND_HOLD"; 392 393 /** 394 * AG call handling feature: accept held or waiting call in three way calling scenarios. 395 * 396 * @hide 397 */ 398 public static final String EXTRA_AG_FEATURE_ACCEPT_HELD_OR_WAITING_CALL = 399 "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_ACCEPT_HELD_OR_WAITING_CALL"; 400 401 /** 402 * AG call handling feature: release held or waiting call in three way calling scenarios. 403 * 404 * @hide 405 */ 406 public static final String EXTRA_AG_FEATURE_RELEASE_HELD_OR_WAITING_CALL = 407 "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_RELEASE_HELD_OR_WAITING_CALL"; 408 409 /** 410 * AG call handling feature: release active call and accept held or waiting call in three way 411 * calling scenarios. 412 * 413 * @hide 414 */ 415 public static final String EXTRA_AG_FEATURE_RELEASE_AND_ACCEPT = 416 "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_RELEASE_AND_ACCEPT"; 417 418 /** 419 * AG call handling feature: merge two calls, held and active - multi party conference mode. 420 * 421 * @hide 422 */ 423 public static final String EXTRA_AG_FEATURE_MERGE = 424 "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_MERGE"; 425 426 /** 427 * AG call handling feature: merge calls and disconnect from multi party conversation leaving 428 * peers connected to each other. Note that this feature needs to be supported by mobile network 429 * operator as it requires connection and billing transfer. 430 * 431 * @hide 432 */ 433 public static final String EXTRA_AG_FEATURE_MERGE_AND_DETACH = 434 "android.bluetooth.headsetclient.extra.EXTRA_AG_FEATURE_MERGE_AND_DETACH"; 435 436 /* Action result codes */ 437 /** @hide */ 438 public static final int ACTION_RESULT_OK = 0; 439 440 /** @hide */ 441 public static final int ACTION_RESULT_ERROR = 1; 442 443 /** @hide */ 444 public static final int ACTION_RESULT_ERROR_NO_CARRIER = 2; 445 446 /** @hide */ 447 public static final int ACTION_RESULT_ERROR_BUSY = 3; 448 449 /** @hide */ 450 public static final int ACTION_RESULT_ERROR_NO_ANSWER = 4; 451 452 /** @hide */ 453 public static final int ACTION_RESULT_ERROR_DELAYED = 5; 454 455 /** @hide */ 456 public static final int ACTION_RESULT_ERROR_BLACKLISTED = 6; 457 458 /** @hide */ 459 public static final int ACTION_RESULT_ERROR_CME = 7; 460 461 /* Detailed CME error codes */ 462 /** @hide */ 463 public static final int CME_PHONE_FAILURE = 0; 464 465 /** @hide */ 466 public static final int CME_NO_CONNECTION_TO_PHONE = 1; 467 468 /** @hide */ 469 public static final int CME_OPERATION_NOT_ALLOWED = 3; 470 471 /** @hide */ 472 public static final int CME_OPERATION_NOT_SUPPORTED = 4; 473 474 /** @hide */ 475 public static final int CME_PHSIM_PIN_REQUIRED = 5; 476 477 /** @hide */ 478 public static final int CME_PHFSIM_PIN_REQUIRED = 6; 479 480 /** @hide */ 481 public static final int CME_PHFSIM_PUK_REQUIRED = 7; 482 483 /** @hide */ 484 public static final int CME_SIM_NOT_INSERTED = 10; 485 486 /** @hide */ 487 public static final int CME_SIM_PIN_REQUIRED = 11; 488 489 /** @hide */ 490 public static final int CME_SIM_PUK_REQUIRED = 12; 491 492 /** @hide */ 493 public static final int CME_SIM_FAILURE = 13; 494 495 /** @hide */ 496 public static final int CME_SIM_BUSY = 14; 497 498 /** @hide */ 499 public static final int CME_SIM_WRONG = 15; 500 501 /** @hide */ 502 public static final int CME_INCORRECT_PASSWORD = 16; 503 504 /** @hide */ 505 public static final int CME_SIM_PIN2_REQUIRED = 17; 506 507 /** @hide */ 508 public static final int CME_SIM_PUK2_REQUIRED = 18; 509 510 /** @hide */ 511 public static final int CME_MEMORY_FULL = 20; 512 513 /** @hide */ 514 public static final int CME_INVALID_INDEX = 21; 515 516 /** @hide */ 517 public static final int CME_NOT_FOUND = 22; 518 519 /** @hide */ 520 public static final int CME_MEMORY_FAILURE = 23; 521 522 /** @hide */ 523 public static final int CME_TEXT_STRING_TOO_LONG = 24; 524 525 /** @hide */ 526 public static final int CME_INVALID_CHARACTER_IN_TEXT_STRING = 25; 527 528 /** @hide */ 529 public static final int CME_DIAL_STRING_TOO_LONG = 26; 530 531 /** @hide */ 532 public static final int CME_INVALID_CHARACTER_IN_DIAL_STRING = 27; 533 534 /** @hide */ 535 public static final int CME_NO_NETWORK_SERVICE = 30; 536 537 /** @hide */ 538 public static final int CME_NETWORK_TIMEOUT = 31; 539 540 /** @hide */ 541 public static final int CME_EMERGENCY_SERVICE_ONLY = 32; 542 543 /** @hide */ 544 public static final int CME_NO_SIMULTANEOUS_VOIP_CS_CALLS = 33; 545 546 /** @hide */ 547 public static final int CME_NOT_SUPPORTED_FOR_VOIP = 34; 548 549 /** @hide */ 550 public static final int CME_SIP_RESPONSE_CODE = 35; 551 552 /** @hide */ 553 public static final int CME_NETWORK_PERSONALIZATION_PIN_REQUIRED = 40; 554 555 /** @hide */ 556 public static final int CME_NETWORK_PERSONALIZATION_PUK_REQUIRED = 41; 557 558 /** @hide */ 559 public static final int CME_NETWORK_SUBSET_PERSONALIZATION_PIN_REQUIRED = 42; 560 561 /** @hide */ 562 public static final int CME_NETWORK_SUBSET_PERSONALIZATION_PUK_REQUIRED = 43; 563 564 /** @hide */ 565 public static final int CME_SERVICE_PROVIDER_PERSONALIZATION_PIN_REQUIRED = 44; 566 567 /** @hide */ 568 public static final int CME_SERVICE_PROVIDER_PERSONALIZATION_PUK_REQUIRED = 45; 569 570 /** @hide */ 571 public static final int CME_CORPORATE_PERSONALIZATION_PIN_REQUIRED = 46; 572 573 /** @hide */ 574 public static final int CME_CORPORATE_PERSONALIZATION_PUK_REQUIRED = 47; 575 576 /** @hide */ 577 public static final int CME_HIDDEN_KEY_REQUIRED = 48; 578 579 /** @hide */ 580 public static final int CME_EAP_NOT_SUPPORTED = 49; 581 582 /** @hide */ 583 public static final int CME_INCORRECT_PARAMETERS = 50; 584 585 /* Action policy for other calls when accepting call */ 586 /** @hide */ 587 public static final int CALL_ACCEPT_NONE = 0; 588 589 /** @hide */ 590 public static final int CALL_ACCEPT_HOLD = 1; 591 592 /** @hide */ 593 public static final int CALL_ACCEPT_TERMINATE = 2; 594 595 private final BluetoothAdapter mAdapter; 596 private final AttributionSource mAttributionSource; 597 598 private IBluetoothHeadsetClient mService; 599 600 /** Create a BluetoothHeadsetClient proxy object. */ BluetoothHeadsetClient(Context context, BluetoothAdapter adapter)601 BluetoothHeadsetClient(Context context, BluetoothAdapter adapter) { 602 mAdapter = adapter; 603 mAttributionSource = adapter.getAttributionSource(); 604 mService = null; 605 mCloseGuard = new CloseGuard(); 606 mCloseGuard.open("close"); 607 } 608 609 /** 610 * Close the connection to the backing service. Other public functions of BluetoothHeadsetClient 611 * will return default error results once close() has been called. Multiple invocations of 612 * close() are ok. 613 * 614 * @hide 615 */ 616 @Override close()617 public void close() { 618 if (VDBG) log("close()"); 619 mAdapter.closeProfileProxy(this); 620 if (mCloseGuard != null) { 621 mCloseGuard.close(); 622 } 623 } 624 625 /** @hide */ 626 @Override onServiceConnected(IBinder service)627 public void onServiceConnected(IBinder service) { 628 mService = IBluetoothHeadsetClient.Stub.asInterface(service); 629 } 630 631 /** @hide */ 632 @Override onServiceDisconnected()633 public void onServiceDisconnected() { 634 mService = null; 635 } 636 getService()637 private IBluetoothHeadsetClient getService() { 638 return mService; 639 } 640 641 /** @hide */ 642 @Override getAdapter()643 public BluetoothAdapter getAdapter() { 644 return mAdapter; 645 } 646 647 /** @hide */ 648 @SuppressWarnings("Finalize") // TODO(b/314811467) finalize()649 protected void finalize() { 650 if (mCloseGuard != null) { 651 mCloseGuard.warnIfOpen(); 652 } 653 close(); 654 } 655 656 /** 657 * Connects to remote device. 658 * 659 * <p>Currently, the system supports only 1 connection. So, in case of the second connection, 660 * this implementation will disconnect already connected device automatically and will process 661 * the new one. 662 * 663 * @param device a remote device we want connect to 664 * @return <code>true</code> if command has been issued successfully; <code>false</code> 665 * otherwise; upon completion HFP sends {@link #ACTION_CONNECTION_STATE_CHANGED} intent. 666 * @hide 667 */ 668 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 669 @RequiresBluetoothConnectPermission 670 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) connect(BluetoothDevice device)671 public boolean connect(BluetoothDevice device) { 672 if (DBG) log("connect(" + device + ")"); 673 final IBluetoothHeadsetClient service = getService(); 674 if (service == null) { 675 Log.w(TAG, "Proxy not attached to service"); 676 if (DBG) log(Log.getStackTraceString(new Throwable())); 677 } else if (isEnabled() && isValidDevice(device)) { 678 try { 679 return service.connect(device, mAttributionSource); 680 } catch (RemoteException e) { 681 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 682 } 683 } 684 return false; 685 } 686 687 /** 688 * Disconnects remote device 689 * 690 * @param device a remote device we want disconnect 691 * @return <code>true</code> if command has been issued successfully; <code>false</code> 692 * otherwise; upon completion HFP sends {@link #ACTION_CONNECTION_STATE_CHANGED} intent. 693 * @hide 694 */ 695 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 696 @RequiresBluetoothConnectPermission 697 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) disconnect(BluetoothDevice device)698 public boolean disconnect(BluetoothDevice device) { 699 if (DBG) log("disconnect(" + device + ")"); 700 final IBluetoothHeadsetClient service = getService(); 701 if (service == null) { 702 Log.w(TAG, "Proxy not attached to service"); 703 if (DBG) log(Log.getStackTraceString(new Throwable())); 704 } else if (isEnabled() && isValidDevice(device)) { 705 try { 706 return service.disconnect(device, mAttributionSource); 707 } catch (RemoteException e) { 708 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 709 } 710 } 711 return false; 712 } 713 714 /** 715 * {@inheritDoc} 716 * 717 * @hide 718 */ 719 @SystemApi 720 @Override 721 @RequiresBluetoothConnectPermission 722 @RequiresPermission( 723 allOf = { 724 android.Manifest.permission.BLUETOOTH_CONNECT, 725 android.Manifest.permission.BLUETOOTH_PRIVILEGED, 726 }) getConnectedDevices()727 public @NonNull List<BluetoothDevice> getConnectedDevices() { 728 if (VDBG) log("getConnectedDevices()"); 729 final IBluetoothHeadsetClient service = getService(); 730 if (service == null) { 731 Log.w(TAG, "Proxy not attached to service"); 732 if (DBG) log(Log.getStackTraceString(new Throwable())); 733 } else if (isEnabled()) { 734 try { 735 return Attributable.setAttributionSource( 736 service.getConnectedDevices(mAttributionSource), mAttributionSource); 737 } catch (RemoteException e) { 738 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 739 throw e.rethrowAsRuntimeException(); 740 } 741 } 742 return Collections.emptyList(); 743 } 744 745 /** 746 * {@inheritDoc} 747 * 748 * @hide 749 */ 750 @SystemApi 751 @Override 752 @RequiresBluetoothConnectPermission 753 @RequiresPermission( 754 allOf = { 755 android.Manifest.permission.BLUETOOTH_CONNECT, 756 android.Manifest.permission.BLUETOOTH_PRIVILEGED, 757 }) 758 @NonNull getDevicesMatchingConnectionStates(@onNull int[] states)759 public List<BluetoothDevice> getDevicesMatchingConnectionStates(@NonNull int[] states) { 760 if (VDBG) log("getDevicesMatchingStates()"); 761 final IBluetoothHeadsetClient service = getService(); 762 if (service == null) { 763 Log.w(TAG, "Proxy not attached to service"); 764 if (DBG) log(Log.getStackTraceString(new Throwable())); 765 } else if (isEnabled()) { 766 try { 767 return Attributable.setAttributionSource( 768 service.getDevicesMatchingConnectionStates(states, mAttributionSource), 769 mAttributionSource); 770 } catch (RemoteException e) { 771 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 772 throw e.rethrowAsRuntimeException(); 773 } 774 } 775 return Collections.emptyList(); 776 } 777 778 /** 779 * {@inheritDoc} 780 * 781 * @hide 782 */ 783 @SystemApi 784 @Override 785 @RequiresBluetoothConnectPermission 786 @RequiresPermission( 787 allOf = { 788 android.Manifest.permission.BLUETOOTH_CONNECT, 789 android.Manifest.permission.BLUETOOTH_PRIVILEGED, 790 }) getConnectionState(@onNull BluetoothDevice device)791 public @BtProfileState int getConnectionState(@NonNull BluetoothDevice device) { 792 if (VDBG) log("getConnectionState(" + device + ")"); 793 final IBluetoothHeadsetClient service = getService(); 794 if (service == null) { 795 Log.w(TAG, "Proxy not attached to service"); 796 if (DBG) log(Log.getStackTraceString(new Throwable())); 797 } else if (isEnabled() && isValidDevice(device)) { 798 try { 799 return service.getConnectionState(device, mAttributionSource); 800 } catch (RemoteException e) { 801 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 802 throw e.rethrowAsRuntimeException(); 803 } 804 } 805 return BluetoothProfile.STATE_DISCONNECTED; 806 } 807 808 /** 809 * Set priority of the profile 810 * 811 * <p>The device should already be paired. Priority can be one of {@link #PRIORITY_ON} or {@link 812 * #PRIORITY_OFF} 813 * 814 * @param device Paired bluetooth device 815 * @return true if priority is set, false on error 816 * @hide 817 */ 818 @RequiresBluetoothConnectPermission 819 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) setPriority(BluetoothDevice device, int priority)820 public boolean setPriority(BluetoothDevice device, int priority) { 821 if (DBG) log("setPriority(" + device + ", " + priority + ")"); 822 return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority)); 823 } 824 825 /** 826 * Set connection policy of the profile 827 * 828 * <p>The device should already be paired. Connection policy can be one of {@link 829 * #CONNECTION_POLICY_ALLOWED}, {@link #CONNECTION_POLICY_FORBIDDEN}, {@link 830 * #CONNECTION_POLICY_UNKNOWN} 831 * 832 * @param device Paired bluetooth device 833 * @param connectionPolicy is the connection policy to set to for this profile 834 * @return true if connectionPolicy is set, false on error 835 * @hide 836 */ 837 @SystemApi 838 @RequiresBluetoothConnectPermission 839 @RequiresPermission( 840 allOf = { 841 android.Manifest.permission.BLUETOOTH_CONNECT, 842 android.Manifest.permission.BLUETOOTH_PRIVILEGED, 843 }) setConnectionPolicy( @onNull BluetoothDevice device, @ConnectionPolicy int connectionPolicy)844 public boolean setConnectionPolicy( 845 @NonNull BluetoothDevice device, @ConnectionPolicy int connectionPolicy) { 846 if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); 847 final IBluetoothHeadsetClient service = getService(); 848 if (service == null) { 849 Log.w(TAG, "Proxy not attached to service"); 850 if (DBG) log(Log.getStackTraceString(new Throwable())); 851 } else if (isEnabled() 852 && isValidDevice(device) 853 && (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN 854 || connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) { 855 try { 856 return service.setConnectionPolicy(device, connectionPolicy, mAttributionSource); 857 } catch (RemoteException e) { 858 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 859 throw e.rethrowAsRuntimeException(); 860 } 861 } 862 return false; 863 } 864 865 /** 866 * Get the priority of the profile. 867 * 868 * <p>The priority can be any of: {@link #PRIORITY_OFF}, {@link #PRIORITY_ON}, {@link 869 * #PRIORITY_UNDEFINED} 870 * 871 * @param device Bluetooth device 872 * @return priority of the device 873 * @hide 874 */ 875 @RequiresLegacyBluetoothPermission 876 @RequiresBluetoothConnectPermission 877 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) getPriority(BluetoothDevice device)878 public int getPriority(BluetoothDevice device) { 879 if (VDBG) log("getPriority(" + device + ")"); 880 return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device)); 881 } 882 883 /** 884 * Get the connection policy of the profile. 885 * 886 * <p>The connection policy can be any of: {@link #CONNECTION_POLICY_ALLOWED}, {@link 887 * #CONNECTION_POLICY_FORBIDDEN}, {@link #CONNECTION_POLICY_UNKNOWN} 888 * 889 * @param device Bluetooth device 890 * @return connection policy of the device 891 * @hide 892 */ 893 @SystemApi 894 @RequiresLegacyBluetoothPermission 895 @RequiresBluetoothConnectPermission 896 @RequiresPermission( 897 allOf = { 898 android.Manifest.permission.BLUETOOTH_CONNECT, 899 android.Manifest.permission.BLUETOOTH_PRIVILEGED, 900 }) getConnectionPolicy(@onNull BluetoothDevice device)901 public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) { 902 if (VDBG) log("getConnectionPolicy(" + device + ")"); 903 final IBluetoothHeadsetClient service = getService(); 904 if (service == null) { 905 Log.w(TAG, "Proxy not attached to service"); 906 if (DBG) log(Log.getStackTraceString(new Throwable())); 907 } else if (isEnabled() && isValidDevice(device)) { 908 try { 909 return service.getConnectionPolicy(device, mAttributionSource); 910 } catch (RemoteException e) { 911 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 912 throw e.rethrowAsRuntimeException(); 913 } 914 } 915 return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; 916 } 917 918 /** 919 * Starts voice recognition. 920 * 921 * @param device remote device 922 * @return <code>true</code> if command has been issued successfully; <code>false</code> 923 * otherwise; upon completion HFP sends {@link #ACTION_AG_EVENT} intent. 924 * <p>Feature required for successful execution is being reported by: {@link 925 * #EXTRA_AG_FEATURE_VOICE_RECOGNITION}. This method invocation will fail silently when 926 * feature is not supported. 927 * @hide 928 */ 929 @RequiresBluetoothConnectPermission 930 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) startVoiceRecognition(BluetoothDevice device)931 public boolean startVoiceRecognition(BluetoothDevice device) { 932 if (DBG) log("startVoiceRecognition()"); 933 final IBluetoothHeadsetClient service = getService(); 934 if (service == null) { 935 Log.w(TAG, "Proxy not attached to service"); 936 if (DBG) log(Log.getStackTraceString(new Throwable())); 937 } else if (isEnabled() && isValidDevice(device)) { 938 try { 939 return service.startVoiceRecognition(device, mAttributionSource); 940 } catch (RemoteException e) { 941 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 942 } 943 } 944 return false; 945 } 946 947 /** 948 * Send vendor specific AT command. 949 * 950 * @param device remote device 951 * @param vendorId vendor number by Bluetooth SIG 952 * @param atCommand command to be sent. It start with + prefix and only one command at one time. 953 * @return <code>true</code> if command has been issued successfully; <code>false</code> 954 * otherwise. 955 * @hide 956 */ 957 @RequiresBluetoothConnectPermission 958 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) sendVendorAtCommand(BluetoothDevice device, int vendorId, String atCommand)959 public boolean sendVendorAtCommand(BluetoothDevice device, int vendorId, String atCommand) { 960 if (DBG) log("sendVendorSpecificCommand()"); 961 final IBluetoothHeadsetClient service = getService(); 962 if (service == null) { 963 Log.w(TAG, "Proxy not attached to service"); 964 if (DBG) log(Log.getStackTraceString(new Throwable())); 965 } else if (isEnabled() && isValidDevice(device)) { 966 try { 967 return service.sendVendorAtCommand(device, vendorId, atCommand, mAttributionSource); 968 } catch (RemoteException e) { 969 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 970 } 971 } 972 return false; 973 } 974 975 /** 976 * Stops voice recognition. 977 * 978 * @param device remote device 979 * @return <code>true</code> if command has been issued successfully; <code>false</code> 980 * otherwise; upon completion HFP sends {@link #ACTION_AG_EVENT} intent. 981 * <p>Feature required for successful execution is being reported by: {@link 982 * #EXTRA_AG_FEATURE_VOICE_RECOGNITION}. This method invocation will fail silently when 983 * feature is not supported. 984 * @hide 985 */ 986 @RequiresBluetoothConnectPermission 987 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) stopVoiceRecognition(BluetoothDevice device)988 public boolean stopVoiceRecognition(BluetoothDevice device) { 989 if (DBG) log("stopVoiceRecognition()"); 990 final IBluetoothHeadsetClient service = getService(); 991 if (service == null) { 992 Log.w(TAG, "Proxy not attached to service"); 993 if (DBG) log(Log.getStackTraceString(new Throwable())); 994 } else if (isEnabled() && isValidDevice(device)) { 995 try { 996 return service.stopVoiceRecognition(device, mAttributionSource); 997 } catch (RemoteException e) { 998 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 999 } 1000 } 1001 return false; 1002 } 1003 1004 /** 1005 * Returns list of all calls in any state. 1006 * 1007 * @param device remote device 1008 * @return list of calls; empty list if none call exists 1009 * @hide 1010 */ 1011 @RequiresBluetoothConnectPermission 1012 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) getCurrentCalls(BluetoothDevice device)1013 public List<BluetoothHeadsetClientCall> getCurrentCalls(BluetoothDevice device) { 1014 if (DBG) log("getCurrentCalls()"); 1015 final IBluetoothHeadsetClient service = getService(); 1016 if (service == null) { 1017 Log.w(TAG, "Proxy not attached to service"); 1018 if (DBG) log(Log.getStackTraceString(new Throwable())); 1019 } else if (isEnabled() && isValidDevice(device)) { 1020 try { 1021 return Attributable.setAttributionSource( 1022 service.getCurrentCalls(device, mAttributionSource), mAttributionSource); 1023 } catch (RemoteException e) { 1024 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 1025 } 1026 } 1027 return null; 1028 } 1029 1030 /** 1031 * Returns list of current values of AG indicators. 1032 * 1033 * @param device remote device 1034 * @return bundle of AG indicators; null if device is not in CONNECTED state 1035 * @hide 1036 */ 1037 @RequiresBluetoothConnectPermission 1038 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) getCurrentAgEvents(BluetoothDevice device)1039 public Bundle getCurrentAgEvents(BluetoothDevice device) { 1040 if (DBG) log("getCurrentAgEvents()"); 1041 final IBluetoothHeadsetClient service = getService(); 1042 if (service == null) { 1043 Log.w(TAG, "Proxy not attached to service"); 1044 if (DBG) log(Log.getStackTraceString(new Throwable())); 1045 } else if (isEnabled() && isValidDevice(device)) { 1046 try { 1047 return service.getCurrentAgEvents(device, mAttributionSource); 1048 } catch (RemoteException e) { 1049 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 1050 } 1051 } 1052 return null; 1053 } 1054 1055 /** 1056 * Accepts a call 1057 * 1058 * @param device remote device 1059 * @param flag action policy while accepting a call. Possible values {@link #CALL_ACCEPT_NONE}, 1060 * {@link #CALL_ACCEPT_HOLD}, {@link #CALL_ACCEPT_TERMINATE} 1061 * @return <code>true</code> if command has been issued successfully; <code>false</code> 1062 * otherwise; upon completion HFP sends {@link #ACTION_CALL_CHANGED} intent. 1063 * @hide 1064 */ 1065 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 1066 @RequiresBluetoothConnectPermission 1067 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) acceptCall(BluetoothDevice device, int flag)1068 public boolean acceptCall(BluetoothDevice device, int flag) { 1069 if (DBG) log("acceptCall()"); 1070 final IBluetoothHeadsetClient service = getService(); 1071 if (service == null) { 1072 Log.w(TAG, "Proxy not attached to service"); 1073 if (DBG) log(Log.getStackTraceString(new Throwable())); 1074 } else if (isEnabled() && isValidDevice(device)) { 1075 try { 1076 return service.acceptCall(device, flag, mAttributionSource); 1077 } catch (RemoteException e) { 1078 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 1079 } 1080 } 1081 return false; 1082 } 1083 1084 /** 1085 * Holds a call. 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_CALL_CHANGED} intent. 1090 * @hide 1091 */ 1092 @RequiresBluetoothConnectPermission 1093 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) holdCall(BluetoothDevice device)1094 public boolean holdCall(BluetoothDevice device) { 1095 if (DBG) log("holdCall()"); 1096 final IBluetoothHeadsetClient service = getService(); 1097 if (service == null) { 1098 Log.w(TAG, "Proxy not attached to service"); 1099 if (DBG) log(Log.getStackTraceString(new Throwable())); 1100 } else if (isEnabled() && isValidDevice(device)) { 1101 try { 1102 return service.holdCall(device, mAttributionSource); 1103 } catch (RemoteException e) { 1104 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 1105 } 1106 } 1107 return false; 1108 } 1109 1110 /** 1111 * Rejects a call. 1112 * 1113 * @param device remote device 1114 * @return <code>true</code> if command has been issued successfully; <code>false</code> 1115 * otherwise; upon completion HFP sends {@link #ACTION_CALL_CHANGED} intent. 1116 * <p>Feature required for successful execution is being reported by: {@link 1117 * #EXTRA_AG_FEATURE_REJECT_CALL}. This method invocation will fail silently when feature is 1118 * not supported. 1119 * @hide 1120 */ 1121 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 1122 @RequiresBluetoothConnectPermission 1123 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) rejectCall(BluetoothDevice device)1124 public boolean rejectCall(BluetoothDevice device) { 1125 if (DBG) log("rejectCall()"); 1126 final IBluetoothHeadsetClient service = getService(); 1127 if (service == null) { 1128 Log.w(TAG, "Proxy not attached to service"); 1129 if (DBG) log(Log.getStackTraceString(new Throwable())); 1130 } else if (isEnabled() && isValidDevice(device)) { 1131 try { 1132 return service.rejectCall(device, mAttributionSource); 1133 } catch (RemoteException e) { 1134 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 1135 } 1136 } 1137 return false; 1138 } 1139 1140 /** 1141 * Terminates a specified call. 1142 * 1143 * <p>Works only when Extended Call Control is supported by Audio Gateway. 1144 * 1145 * @param device remote device 1146 * @param call Handle of call obtained in {@link #dial(BluetoothDevice, String)} or obtained via 1147 * {@link #ACTION_CALL_CHANGED}. {@code call} may be null in which case we will hangup all 1148 * active calls. 1149 * @return <code>true</code> if command has been issued successfully; <code>false</code> 1150 * otherwise; upon completion HFP sends {@link #ACTION_CALL_CHANGED} intent. 1151 * <p>Feature required for successful execution is being reported by: {@link 1152 * #EXTRA_AG_FEATURE_ECC}. This method invocation will fail silently when feature is not 1153 * supported. 1154 * @hide 1155 */ 1156 @RequiresBluetoothConnectPermission 1157 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) terminateCall(BluetoothDevice device, BluetoothHeadsetClientCall call)1158 public boolean terminateCall(BluetoothDevice device, BluetoothHeadsetClientCall call) { 1159 if (DBG) log("terminateCall()"); 1160 final IBluetoothHeadsetClient service = getService(); 1161 if (service == null) { 1162 Log.w(TAG, "Proxy not attached to service"); 1163 if (DBG) log(Log.getStackTraceString(new Throwable())); 1164 } else if (isEnabled() && isValidDevice(device)) { 1165 try { 1166 return service.terminateCall(device, call, mAttributionSource); 1167 } catch (RemoteException e) { 1168 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 1169 } 1170 } 1171 return false; 1172 } 1173 1174 /** 1175 * Enters private mode with a specified call. 1176 * 1177 * <p>Works only when Extended Call Control is supported by Audio Gateway. 1178 * 1179 * @param device remote device 1180 * @param index index of the call to connect in private mode 1181 * @return <code>true</code> if command has been issued successfully; <code>false</code> 1182 * otherwise; upon completion HFP sends {@link #ACTION_CALL_CHANGED} intent. 1183 * <p>Feature required for successful execution is being reported by: {@link 1184 * #EXTRA_AG_FEATURE_ECC}. This method invocation will fail silently when feature is not 1185 * supported. 1186 * @hide 1187 */ 1188 @RequiresBluetoothConnectPermission 1189 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) enterPrivateMode(BluetoothDevice device, int index)1190 public boolean enterPrivateMode(BluetoothDevice device, int index) { 1191 if (DBG) log("enterPrivateMode()"); 1192 final IBluetoothHeadsetClient service = getService(); 1193 if (service == null) { 1194 Log.w(TAG, "Proxy not attached to service"); 1195 if (DBG) log(Log.getStackTraceString(new Throwable())); 1196 } else if (isEnabled() && isValidDevice(device)) { 1197 try { 1198 return service.enterPrivateMode(device, index, mAttributionSource); 1199 } catch (RemoteException e) { 1200 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 1201 } 1202 } 1203 return false; 1204 } 1205 1206 /** 1207 * Performs explicit call transfer. 1208 * 1209 * <p>That means connect other calls and disconnect. 1210 * 1211 * @param device remote device 1212 * @return <code>true</code> if command has been issued successfully; <code>false</code> 1213 * otherwise; upon completion HFP sends {@link #ACTION_CALL_CHANGED} intent. 1214 * <p>Feature required for successful execution is being reported by: {@link 1215 * #EXTRA_AG_FEATURE_MERGE_AND_DETACH}. This method invocation will fail silently when 1216 * feature is not supported. 1217 * @hide 1218 */ 1219 @RequiresBluetoothConnectPermission 1220 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) explicitCallTransfer(BluetoothDevice device)1221 public boolean explicitCallTransfer(BluetoothDevice device) { 1222 if (DBG) log("explicitCallTransfer()"); 1223 final IBluetoothHeadsetClient service = getService(); 1224 if (service == null) { 1225 Log.w(TAG, "Proxy not attached to service"); 1226 if (DBG) log(Log.getStackTraceString(new Throwable())); 1227 } else if (isEnabled() && isValidDevice(device)) { 1228 try { 1229 return service.explicitCallTransfer(device, mAttributionSource); 1230 } catch (RemoteException e) { 1231 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 1232 } 1233 } 1234 return false; 1235 } 1236 1237 /** 1238 * Places a call with specified number. 1239 * 1240 * @param device remote device 1241 * @param number valid phone number 1242 * @return <code>{@link BluetoothHeadsetClientCall} call</code> if command has been issued 1243 * successfully; <code>{@code null}</code> otherwise; upon completion HFP sends {@link 1244 * #ACTION_CALL_CHANGED} intent in case of success; {@link #ACTION_RESULT} is sent 1245 * otherwise; 1246 * @hide 1247 */ 1248 @RequiresBluetoothConnectPermission 1249 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) dial(BluetoothDevice device, String number)1250 public BluetoothHeadsetClientCall dial(BluetoothDevice device, String number) { 1251 if (DBG) log("dial()"); 1252 final IBluetoothHeadsetClient service = getService(); 1253 if (service == null) { 1254 Log.w(TAG, "Proxy not attached to service"); 1255 if (DBG) log(Log.getStackTraceString(new Throwable())); 1256 } else if (isEnabled() && isValidDevice(device)) { 1257 try { 1258 return Attributable.setAttributionSource( 1259 service.dial(device, number, mAttributionSource), mAttributionSource); 1260 } catch (RemoteException e) { 1261 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 1262 } 1263 } 1264 return null; 1265 } 1266 1267 /** 1268 * Sends DTMF code. 1269 * 1270 * <p>Possible code values : 0,1,2,3,4,5,6,7,8,9,A,B,C,D,*,# 1271 * 1272 * @param device remote device 1273 * @param code ASCII code 1274 * @return <code>true</code> if command has been issued successfully; <code>false</code> 1275 * otherwise; upon completion HFP sends {@link #ACTION_RESULT} intent; 1276 * @hide 1277 */ 1278 @RequiresBluetoothConnectPermission 1279 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) sendDTMF(BluetoothDevice device, byte code)1280 public boolean sendDTMF(BluetoothDevice device, byte code) { 1281 if (DBG) log("sendDTMF()"); 1282 final IBluetoothHeadsetClient service = getService(); 1283 if (service == null) { 1284 Log.w(TAG, "Proxy not attached to service"); 1285 if (DBG) log(Log.getStackTraceString(new Throwable())); 1286 } else if (isEnabled() && isValidDevice(device)) { 1287 try { 1288 return service.sendDTMF(device, code, mAttributionSource); 1289 } catch (RemoteException e) { 1290 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 1291 } 1292 } 1293 return false; 1294 } 1295 1296 /** 1297 * Get a number corresponding to last voice tag recorded on AG. 1298 * 1299 * @param device remote device 1300 * @return <code>true</code> if command has been issued successfully; <code>false</code> 1301 * otherwise; upon completion HFP sends {@link #ACTION_LAST_VTAG} or {@link #ACTION_RESULT} 1302 * intent; 1303 * <p>Feature required for successful execution is being reported by: {@link 1304 * #EXTRA_AG_FEATURE_ATTACH_NUMBER_TO_VT}. This method invocation will fail silently when 1305 * feature is not supported. 1306 * @hide 1307 */ 1308 @RequiresBluetoothConnectPermission 1309 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) getLastVoiceTagNumber(BluetoothDevice device)1310 public boolean getLastVoiceTagNumber(BluetoothDevice device) { 1311 if (DBG) log("getLastVoiceTagNumber()"); 1312 final IBluetoothHeadsetClient service = getService(); 1313 if (service == null) { 1314 Log.w(TAG, "Proxy not attached to service"); 1315 if (DBG) log(Log.getStackTraceString(new Throwable())); 1316 } else if (isEnabled() && isValidDevice(device)) { 1317 try { 1318 return service.getLastVoiceTagNumber(device, mAttributionSource); 1319 } catch (RemoteException e) { 1320 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 1321 } 1322 } 1323 return false; 1324 } 1325 1326 /** 1327 * Returns current audio state of Audio Gateway. 1328 * 1329 * <p>Note: This is an internal function and shouldn't be exposed 1330 * 1331 * @hide 1332 */ 1333 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 1334 @RequiresBluetoothConnectPermission 1335 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) getAudioState(BluetoothDevice device)1336 public int getAudioState(BluetoothDevice device) { 1337 if (VDBG) log("getAudioState"); 1338 final IBluetoothHeadsetClient service = getService(); 1339 if (service == null) { 1340 Log.w(TAG, "Proxy not attached to service"); 1341 if (DBG) log(Log.getStackTraceString(new Throwable())); 1342 } else if (isEnabled()) { 1343 try { 1344 return service.getAudioState(device, mAttributionSource); 1345 } catch (RemoteException e) { 1346 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 1347 } 1348 } 1349 return BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED; 1350 } 1351 1352 /** 1353 * Sets whether audio routing is allowed. 1354 * 1355 * @param device remote device 1356 * @param allowed if routing is allowed to the device Note: This is an internal function and 1357 * shouldn't be exposed 1358 * @hide 1359 */ 1360 @RequiresBluetoothConnectPermission 1361 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) setAudioRouteAllowed(BluetoothDevice device, boolean allowed)1362 public void setAudioRouteAllowed(BluetoothDevice device, boolean allowed) { 1363 if (VDBG) log("setAudioRouteAllowed"); 1364 final IBluetoothHeadsetClient service = getService(); 1365 if (service == null) { 1366 Log.w(TAG, "Proxy not attached to service"); 1367 if (DBG) log(Log.getStackTraceString(new Throwable())); 1368 } else if (isEnabled()) { 1369 try { 1370 service.setAudioRouteAllowed(device, allowed, mAttributionSource); 1371 } catch (RemoteException e) { 1372 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 1373 } 1374 } 1375 } 1376 1377 /** 1378 * Returns whether audio routing is allowed. 1379 * 1380 * @param device remote device 1381 * @return whether the command succeeded Note: This is an internal function and shouldn't be 1382 * exposed 1383 * @hide 1384 */ 1385 @RequiresBluetoothConnectPermission 1386 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) getAudioRouteAllowed(BluetoothDevice device)1387 public boolean getAudioRouteAllowed(BluetoothDevice device) { 1388 if (VDBG) log("getAudioRouteAllowed"); 1389 final IBluetoothHeadsetClient service = getService(); 1390 if (service == null) { 1391 Log.w(TAG, "Proxy not attached to service"); 1392 if (DBG) log(Log.getStackTraceString(new Throwable())); 1393 } else if (isEnabled()) { 1394 try { 1395 return service.getAudioRouteAllowed(device, mAttributionSource); 1396 } catch (RemoteException e) { 1397 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 1398 } 1399 } 1400 return false; 1401 } 1402 1403 /** 1404 * Initiates a connection of audio channel. 1405 * 1406 * <p>It setup SCO channel with remote connected Handsfree AG device. 1407 * 1408 * @param device remote device 1409 * @return <code>true</code> if command has been issued successfully; <code>false</code> 1410 * otherwise; upon completion HFP sends {@link #ACTION_AUDIO_STATE_CHANGED} intent; 1411 * @hide 1412 */ 1413 @RequiresBluetoothConnectPermission 1414 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) connectAudio(BluetoothDevice device)1415 public boolean connectAudio(BluetoothDevice device) { 1416 if (VDBG) log("connectAudio"); 1417 final IBluetoothHeadsetClient service = getService(); 1418 if (service == null) { 1419 Log.w(TAG, "Proxy not attached to service"); 1420 if (DBG) log(Log.getStackTraceString(new Throwable())); 1421 } else if (isEnabled()) { 1422 try { 1423 return service.connectAudio(device, mAttributionSource); 1424 } catch (RemoteException e) { 1425 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 1426 } 1427 } 1428 return false; 1429 } 1430 1431 /** 1432 * Disconnects audio channel. 1433 * 1434 * <p>It tears down the SCO channel from remote AG device. 1435 * 1436 * @param device remote device 1437 * @return <code>true</code> if command has been issued successfully; <code>false</code> 1438 * otherwise; upon completion HFP sends {@link #ACTION_AUDIO_STATE_CHANGED} intent; 1439 * @hide 1440 */ 1441 @RequiresBluetoothConnectPermission 1442 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) disconnectAudio(BluetoothDevice device)1443 public boolean disconnectAudio(BluetoothDevice device) { 1444 if (VDBG) log("disconnectAudio"); 1445 final IBluetoothHeadsetClient service = getService(); 1446 if (service == null) { 1447 Log.w(TAG, "Proxy not attached to service"); 1448 if (DBG) log(Log.getStackTraceString(new Throwable())); 1449 } else if (isEnabled()) { 1450 try { 1451 return service.disconnectAudio(device, mAttributionSource); 1452 } catch (RemoteException e) { 1453 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 1454 } 1455 } 1456 return false; 1457 } 1458 1459 /** 1460 * Get Audio Gateway features 1461 * 1462 * @param device remote device 1463 * @return bundle of AG features; null if no service or AG not connected 1464 * @hide 1465 */ 1466 @RequiresBluetoothConnectPermission 1467 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) getCurrentAgFeatures(BluetoothDevice device)1468 public Bundle getCurrentAgFeatures(BluetoothDevice device) { 1469 if (VDBG) log("getCurrentAgFeatures"); 1470 final IBluetoothHeadsetClient service = getService(); 1471 if (service == null) { 1472 Log.w(TAG, "Proxy not attached to service"); 1473 if (DBG) log(Log.getStackTraceString(new Throwable())); 1474 } else if (isEnabled()) { 1475 try { 1476 return service.getCurrentAgFeatures(device, mAttributionSource); 1477 } catch (RemoteException e) { 1478 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); 1479 } 1480 } 1481 return null; 1482 } 1483 1484 /** 1485 * A class that contains the network service info provided by the HFP Client profile 1486 * 1487 * @hide 1488 */ 1489 @SystemApi 1490 public static final class NetworkServiceState implements Parcelable { 1491 /** The device associated with this service state */ 1492 private final BluetoothDevice mDevice; 1493 1494 /** True if there is service available, False otherwise */ 1495 private final boolean mIsServiceAvailable; 1496 1497 /** The name of the operator associated with the remote device's current network */ 1498 private final String mOperatorName; 1499 1500 /** The general signal strength, from 0 to 5 */ 1501 private final int mSignalStrength; 1502 1503 /** True if we are network roaming, False otherwise */ 1504 private final boolean mIsRoaming; 1505 1506 /** 1507 * Create a NetworkServiceState Object 1508 * 1509 * @param device The device associated with this network signal state 1510 * @param isServiceAvailable True if there is service available, False otherwise 1511 * @param operatorName The name of the operator associated with the remote device's current 1512 * network. Use Null if the value is unknown 1513 * @param signalStrength The general signal strength 1514 * @param isRoaming True if we are network roaming, False otherwise 1515 * @hide 1516 */ NetworkServiceState( BluetoothDevice device, boolean isServiceAvailable, String operatorName, int signalStrength, boolean isRoaming)1517 public NetworkServiceState( 1518 BluetoothDevice device, 1519 boolean isServiceAvailable, 1520 String operatorName, 1521 int signalStrength, 1522 boolean isRoaming) { 1523 mDevice = device; 1524 mIsServiceAvailable = isServiceAvailable; 1525 mOperatorName = operatorName; 1526 mSignalStrength = signalStrength; 1527 mIsRoaming = isRoaming; 1528 } 1529 1530 /** 1531 * Get the device associated with this network service state 1532 * 1533 * @return a BluetoothDevice associated with this state 1534 * @hide 1535 */ 1536 @SystemApi getDevice()1537 public @NonNull BluetoothDevice getDevice() { 1538 return mDevice; 1539 } 1540 1541 /** 1542 * Get the network service availability state 1543 * 1544 * @return True if there is service available, False otherwise 1545 * @hide 1546 */ 1547 @SystemApi isServiceAvailable()1548 public boolean isServiceAvailable() { 1549 return mIsServiceAvailable; 1550 } 1551 1552 /** 1553 * Get the network operator name 1554 * 1555 * @return A string representing the name of the operator the remote device is on, or null 1556 * if unknown. 1557 * @hide 1558 */ 1559 @SystemApi getNetworkOperatorName()1560 public @Nullable String getNetworkOperatorName() { 1561 return mOperatorName; 1562 } 1563 1564 /** 1565 * The HFP Client defined signal strength, from 0 to 5. 1566 * 1567 * <p>Bluetooth HFP v1.8 specifies that the signal strength of a device can be [0, 5]. It 1568 * does not place any requirements on how a device derives those values. While they're 1569 * typically derived from signal quality/RSSI buckets, there's no way to be certain on the 1570 * exact meaning. Derivation methods can even change between wireless cellular technologies. 1571 * 1572 * <p>That said, you can "generally" interpret the values relative to each other as follows: 1573 * - Level 0: None/Unknown - Level 1: Very Poor - Level 2: Poor - Level 3: Fair - Level 4: 1574 * Good - Level 5: Great 1575 * 1576 * @return the HFP Client defined signal strength, range [0, 5] 1577 * @hide 1578 */ 1579 @SystemApi getSignalStrength()1580 public @IntRange(from = 0, to = 5) int getSignalStrength() { 1581 return mSignalStrength; 1582 } 1583 1584 /** 1585 * Get the network service roaming status 1586 * 1587 * <p>* @return True if we are network roaming, False otherwise 1588 * 1589 * @hide 1590 */ 1591 @SystemApi isRoaming()1592 public boolean isRoaming() { 1593 return mIsRoaming; 1594 } 1595 1596 /** {@link Parcelable.Creator} interface implementation. */ 1597 public static final @NonNull Parcelable.Creator<NetworkServiceState> CREATOR = 1598 new Parcelable.Creator<NetworkServiceState>() { 1599 public NetworkServiceState createFromParcel(Parcel in) { 1600 return new NetworkServiceState( 1601 (BluetoothDevice) in.readParcelable(null), 1602 in.readInt() == 1, 1603 in.readString(), 1604 in.readInt(), 1605 in.readInt() == 1); 1606 } 1607 1608 public @NonNull NetworkServiceState[] newArray(int size) { 1609 return new NetworkServiceState[size]; 1610 } 1611 }; 1612 1613 /** @hide */ 1614 @Override writeToParcel(@onNull Parcel out, int flags)1615 public void writeToParcel(@NonNull Parcel out, int flags) { 1616 out.writeParcelable(mDevice, 0); 1617 out.writeInt(mIsServiceAvailable ? 1 : 0); 1618 out.writeString(mOperatorName); 1619 out.writeInt(mSignalStrength); 1620 out.writeInt(mIsRoaming ? 1 : 0); 1621 } 1622 1623 /** @hide */ 1624 @Override describeContents()1625 public int describeContents() { 1626 return 0; 1627 } 1628 } 1629 1630 /** 1631 * Intent used to broadcast the change in network service state of an HFP Client device 1632 * 1633 * <p>This intent will have 2 extras: 1634 * 1635 * <ul> 1636 * <li>{@link BluetoothDevice#EXTRA_DEVICE} - The remote device. 1637 * <li>{@link EXTRA_NETWORK_SERVICE_STATE} - A {@link NetworkServiceState} object. 1638 * </ul> 1639 * 1640 * @hide 1641 */ 1642 @SuppressLint("ActionValue") 1643 @SystemApi 1644 @RequiresBluetoothConnectPermission 1645 @RequiresPermission( 1646 allOf = { 1647 android.Manifest.permission.BLUETOOTH_CONNECT, 1648 android.Manifest.permission.BLUETOOTH_PRIVILEGED, 1649 }) 1650 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 1651 public static final String ACTION_NETWORK_SERVICE_STATE_CHANGED = 1652 "android.bluetooth.headsetclient.profile.action.NETWORK_SERVICE_STATE_CHANGED"; 1653 1654 /** 1655 * Extra for the network service state changed intent. 1656 * 1657 * <p>This extra represents the current network service state of a connected Bluetooth device. 1658 * 1659 * @hide 1660 */ 1661 @SuppressLint("ActionValue") 1662 @SystemApi 1663 public static final String EXTRA_NETWORK_SERVICE_STATE = 1664 "android.bluetooth.headsetclient.extra.EXTRA_NETWORK_SERVICE_STATE"; 1665 1666 /** 1667 * Get the network service state for a device 1668 * 1669 * @param device The {@link BluetoothDevice} you want the network service state for 1670 * @return A {@link NetworkServiceState} representing the network service state of the device, 1671 * or null if the device is not connected 1672 * @hide 1673 */ 1674 @SystemApi 1675 @RequiresBluetoothConnectPermission 1676 @RequiresPermission( 1677 allOf = { 1678 android.Manifest.permission.BLUETOOTH_CONNECT, 1679 android.Manifest.permission.BLUETOOTH_PRIVILEGED, 1680 }) getNetworkServiceState(@onNull BluetoothDevice device)1681 public @Nullable NetworkServiceState getNetworkServiceState(@NonNull BluetoothDevice device) { 1682 if (device == null) { 1683 return null; 1684 } 1685 1686 Bundle agEvents = getCurrentAgEvents(device); 1687 if (agEvents == null) { 1688 return null; 1689 } 1690 1691 boolean isServiceAvailable = (agEvents.getInt(EXTRA_NETWORK_STATUS, 0) == 1); 1692 int signalStrength = agEvents.getInt(EXTRA_NETWORK_SIGNAL_STRENGTH, 0); 1693 String operatorName = agEvents.getString(EXTRA_OPERATOR_NAME, null); 1694 boolean isRoaming = (agEvents.getInt(EXTRA_NETWORK_ROAMING, 0) == 1); 1695 1696 return new NetworkServiceState( 1697 device, isServiceAvailable, operatorName, signalStrength, isRoaming); 1698 } 1699 isEnabled()1700 private boolean isEnabled() { 1701 return mAdapter.getState() == BluetoothAdapter.STATE_ON; 1702 } 1703 isValidDevice(BluetoothDevice device)1704 private static boolean isValidDevice(BluetoothDevice device) { 1705 return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress()); 1706 } 1707 log(String msg)1708 private static void log(String msg) { 1709 Log.d(TAG, msg); 1710 } 1711 } 1712