1 /* 2 * Copyright (C) 2013 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.bluetooth; 18 19 import android.annotation.FlaggedApi; 20 import android.annotation.IntDef; 21 import android.annotation.NonNull; 22 import android.annotation.RequiresNoPermission; 23 import android.annotation.RequiresPermission; 24 import android.annotation.SuppressLint; 25 import android.bluetooth.BluetoothGattCharacteristic.WriteType; 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.os.Build; 31 import android.os.Handler; 32 import android.os.IBinder; 33 import android.os.ParcelUuid; 34 import android.os.RemoteException; 35 import android.util.Log; 36 37 import com.android.bluetooth.flags.Flags; 38 39 import java.lang.annotation.Retention; 40 import java.lang.annotation.RetentionPolicy; 41 import java.util.ArrayList; 42 import java.util.List; 43 import java.util.UUID; 44 45 /** 46 * Public API for the Bluetooth GATT Profile. 47 * 48 * <p>This class provides Bluetooth GATT functionality to enable communication with Bluetooth Smart 49 * or Smart Ready devices. 50 * 51 * <p>To connect to a remote peripheral device, create a {@link BluetoothGattCallback} and call 52 * {@link BluetoothDevice#connectGatt} to get a instance of this class. GATT capable devices can be 53 * discovered using the Bluetooth device discovery or BLE scan process. 54 */ 55 public final class BluetoothGatt implements BluetoothProfile { 56 private static final String TAG = "BluetoothGatt"; 57 private static final boolean DBG = true; 58 private static final boolean VDBG = false; 59 60 @UnsupportedAppUsage private IBluetoothGatt mService; 61 @UnsupportedAppUsage private volatile BluetoothGattCallback mCallback; 62 private Handler mHandler; 63 @UnsupportedAppUsage private int mClientIf; 64 private BluetoothDevice mDevice; 65 @UnsupportedAppUsage private boolean mAutoConnect; 66 67 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) 68 private int mAuthRetryState; 69 70 private int mConnState; 71 private final Object mStateLock = new Object(); 72 private final Object mDeviceBusyLock = new Object(); 73 74 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 75 private Boolean mDeviceBusy = false; 76 77 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 78 private int mTransport; 79 80 private int mPhy; 81 private boolean mOpportunistic; 82 private final AttributionSource mAttributionSource; 83 84 private static final int AUTH_RETRY_STATE_IDLE = 0; 85 private static final int AUTH_RETRY_STATE_MITM = 2; 86 87 private static final int CONN_STATE_IDLE = 0; 88 private static final int CONN_STATE_CONNECTING = 1; 89 private static final int CONN_STATE_CONNECTED = 2; 90 private static final int CONN_STATE_CLOSED = 4; 91 92 private static final int WRITE_CHARACTERISTIC_MAX_RETRIES = 5; 93 private static final int WRITE_CHARACTERISTIC_TIME_TO_WAIT = 10; // milliseconds 94 // Max length of an attribute value, defined in gatt_api.h 95 private static final int GATT_MAX_ATTR_LEN = 512; 96 97 private List<BluetoothGattService> mServices; 98 99 /** A GATT operation completed successfully */ 100 public static final int GATT_SUCCESS = 0; 101 102 /** GATT read operation is not permitted */ 103 public static final int GATT_READ_NOT_PERMITTED = 0x2; 104 105 /** GATT write operation is not permitted */ 106 public static final int GATT_WRITE_NOT_PERMITTED = 0x3; 107 108 /** Insufficient authentication for a given operation */ 109 public static final int GATT_INSUFFICIENT_AUTHENTICATION = 0x5; 110 111 /** The given request is not supported */ 112 public static final int GATT_REQUEST_NOT_SUPPORTED = 0x6; 113 114 /** Insufficient encryption for a given operation */ 115 public static final int GATT_INSUFFICIENT_ENCRYPTION = 0xf; 116 117 /** A read or write operation was requested with an invalid offset */ 118 public static final int GATT_INVALID_OFFSET = 0x7; 119 120 /** Insufficient authorization for a given operation */ 121 public static final int GATT_INSUFFICIENT_AUTHORIZATION = 0x8; 122 123 /** A write operation exceeds the maximum length of the attribute */ 124 public static final int GATT_INVALID_ATTRIBUTE_LENGTH = 0xd; 125 126 /** A remote device connection is congested. */ 127 public static final int GATT_CONNECTION_CONGESTED = 0x8f; 128 129 /** 130 * GATT connection timed out, likely due to the remote device being out of range or not 131 * advertising as connectable. 132 */ 133 @FlaggedApi(Flags.FLAG_ENUMERATE_GATT_ERRORS) 134 public static final int GATT_CONNECTION_TIMEOUT = 0x93; 135 136 /** A GATT operation failed, errors other than the above */ 137 public static final int GATT_FAILURE = 0x101; 138 139 /** 140 * Connection parameter update - Use the connection parameters recommended by the Bluetooth SIG. 141 * This is the default value if no connection parameter update is requested. 142 */ 143 public static final int CONNECTION_PRIORITY_BALANCED = 0; 144 145 /** 146 * Connection parameter update - Request a high priority, low latency connection. An application 147 * should only request high priority connection parameters to transfer large amounts of data 148 * over LE quickly. Once the transfer is complete, the application should request {@link 149 * BluetoothGatt#CONNECTION_PRIORITY_BALANCED} connection parameters to reduce energy use. 150 */ 151 public static final int CONNECTION_PRIORITY_HIGH = 1; 152 153 /** Connection parameter update - Request low power, reduced data rate connection parameters. */ 154 public static final int CONNECTION_PRIORITY_LOW_POWER = 2; 155 156 /** 157 * Connection parameter update - Request the priority preferred for Digital Car Key for a lower 158 * latency connection. This connection parameter will consume more power than {@link 159 * BluetoothGatt#CONNECTION_PRIORITY_BALANCED}, so it is recommended that apps do not use this 160 * unless it specifically fits their use case. 161 */ 162 public static final int CONNECTION_PRIORITY_DCK = 3; 163 164 /** 165 * Connection subrate request - Balanced. 166 * 167 * @hide 168 */ 169 public static final int SUBRATE_REQUEST_MODE_BALANCED = 0; 170 171 /** 172 * Connection subrate request - High. 173 * 174 * @hide 175 */ 176 public static final int SUBRATE_REQUEST_MODE_HIGH = 1; 177 178 /** 179 * Connection Subrate Request - Low Power. 180 * 181 * @hide 182 */ 183 public static final int SUBRATE_REQUEST_MODE_LOW_POWER = 2; 184 185 /** @hide */ 186 @Retention(RetentionPolicy.SOURCE) 187 @IntDef( 188 prefix = {"SUBRATE_REQUEST_MODE"}, 189 value = { 190 SUBRATE_REQUEST_MODE_BALANCED, 191 SUBRATE_REQUEST_MODE_HIGH, 192 SUBRATE_REQUEST_MODE_LOW_POWER, 193 }) 194 public @interface SubrateRequestMode {} 195 196 /** 197 * No authentication required. 198 * 199 * @hide 200 */ 201 /*package*/ static final int AUTHENTICATION_NONE = 0; 202 203 /** 204 * Authentication requested; no person-in-the-middle protection required. 205 * 206 * @hide 207 */ 208 /*package*/ static final int AUTHENTICATION_NO_MITM = 1; 209 210 /** 211 * Authentication with person-in-the-middle protection requested. 212 * 213 * @hide 214 */ 215 /*package*/ static final int AUTHENTICATION_MITM = 2; 216 217 /** Bluetooth GATT callbacks. Overrides the default BluetoothGattCallback implementation. */ 218 @SuppressLint("AndroidFrameworkBluetoothPermission") 219 private final IBluetoothGattCallback mBluetoothGattCallback = 220 new IBluetoothGattCallback.Stub() { 221 /** 222 * Application interface registered - app is ready to go 223 * 224 * @hide 225 */ 226 @Override 227 @SuppressLint("AndroidFrameworkRequiresPermission") 228 public void onClientRegistered(int status, int clientIf) { 229 if (DBG) { 230 Log.d( 231 TAG, 232 "onClientRegistered() -" 233 + (" status=" + status) 234 + (" clientIf=" + clientIf)); 235 } 236 mClientIf = clientIf; 237 synchronized (mStateLock) { 238 if (mConnState == CONN_STATE_CLOSED) { 239 if (DBG) { 240 Log.d( 241 TAG, 242 "Client registration completed after closed," 243 + " unregistering"); 244 } 245 unregisterApp(); 246 return; 247 } 248 if (VDBG) { 249 if (mConnState != CONN_STATE_CONNECTING) { 250 Log.e(TAG, "Bad connection state: " + mConnState); 251 } 252 } 253 } 254 if (status != GATT_SUCCESS) { 255 runOrQueueCallback( 256 new Runnable() { 257 @Override 258 public void run() { 259 final BluetoothGattCallback callback = mCallback; 260 if (callback != null) { 261 callback.onConnectionStateChange( 262 BluetoothGatt.this, 263 GATT_FAILURE, 264 BluetoothProfile.STATE_DISCONNECTED); 265 } 266 } 267 }); 268 269 synchronized (mStateLock) { 270 mConnState = CONN_STATE_IDLE; 271 } 272 return; 273 } 274 try { 275 // autoConnect is inverse of "isDirect" 276 mService.clientConnect( 277 mClientIf, 278 mDevice.getAddress(), 279 mDevice.getAddressType(), 280 !mAutoConnect, 281 mTransport, 282 mOpportunistic, 283 mPhy, 284 mAttributionSource); 285 } catch (RemoteException e) { 286 Log.e(TAG, "", e); 287 } 288 } 289 290 /** 291 * Phy update callback 292 * 293 * @hide 294 */ 295 @Override 296 public void onPhyUpdate(String address, int txPhy, int rxPhy, int status) { 297 if (DBG) { 298 Log.d( 299 TAG, 300 "onPhyUpdate() -" 301 + (" status=" + status) 302 + (" address=" + address) 303 + (" txPhy=" + txPhy) 304 + (" rxPhy=" + rxPhy)); 305 } 306 if (!address.equals(mDevice.getAddress())) { 307 return; 308 } 309 310 runOrQueueCallback( 311 new Runnable() { 312 @Override 313 public void run() { 314 final BluetoothGattCallback callback = mCallback; 315 if (callback != null) { 316 callback.onPhyUpdate( 317 BluetoothGatt.this, txPhy, rxPhy, status); 318 } 319 } 320 }); 321 } 322 323 /** 324 * Phy read callback 325 * 326 * @hide 327 */ 328 @Override 329 public void onPhyRead(String address, int txPhy, int rxPhy, int status) { 330 if (DBG) { 331 Log.d( 332 TAG, 333 "onPhyRead() -" 334 + (" status=" + status) 335 + (" address=" + address) 336 + (" txPhy=" + txPhy) 337 + (" rxPhy=" + rxPhy)); 338 } 339 if (!address.equals(mDevice.getAddress())) { 340 return; 341 } 342 343 runOrQueueCallback( 344 new Runnable() { 345 @Override 346 public void run() { 347 final BluetoothGattCallback callback = mCallback; 348 if (callback != null) { 349 callback.onPhyRead( 350 BluetoothGatt.this, txPhy, rxPhy, status); 351 } 352 } 353 }); 354 } 355 356 /** 357 * Client connection state changed 358 * 359 * @hide 360 */ 361 @Override 362 public void onClientConnectionState( 363 int status, int clientIf, boolean connected, String address) { 364 if (DBG) { 365 Log.d( 366 TAG, 367 "onClientConnectionState() -" 368 + (" status=" + status) 369 + (" clientIf=" + clientIf) 370 + (" connected=" + connected) 371 + (" device=" + address)); 372 } 373 if (!address.equals(mDevice.getAddress())) { 374 return; 375 } 376 int profileState = 377 connected 378 ? BluetoothProfile.STATE_CONNECTED 379 : BluetoothProfile.STATE_DISCONNECTED; 380 381 runOrQueueCallback( 382 new Runnable() { 383 @Override 384 public void run() { 385 final BluetoothGattCallback callback = mCallback; 386 if (callback != null) { 387 callback.onConnectionStateChange( 388 BluetoothGatt.this, status, profileState); 389 } 390 } 391 }); 392 393 synchronized (mStateLock) { 394 if (connected) { 395 mConnState = CONN_STATE_CONNECTED; 396 } else { 397 mConnState = CONN_STATE_IDLE; 398 } 399 } 400 401 synchronized (mDeviceBusyLock) { 402 mDeviceBusy = false; 403 } 404 } 405 406 /** 407 * Remote search has been completed. The internal object structure should now 408 * reflect the state of the remote device database. Let the application know that we 409 * are done at this point. 410 * 411 * @hide 412 */ 413 @Override 414 public void onSearchComplete( 415 String address, List<BluetoothGattService> services, int status) { 416 if (DBG) { 417 Log.d(TAG, "onSearchComplete() = Device=" + address + " Status=" + status); 418 } 419 if (!address.equals(mDevice.getAddress())) { 420 return; 421 } 422 423 for (BluetoothGattService s : services) { 424 // services we receive don't have device set properly. 425 s.setDevice(mDevice); 426 } 427 428 mServices.addAll(services); 429 430 // Fix references to included services, as they doesn't point to right objects. 431 for (BluetoothGattService fixedService : mServices) { 432 ArrayList<BluetoothGattService> includedServices = 433 new ArrayList(fixedService.getIncludedServices()); 434 fixedService.getIncludedServices().clear(); 435 436 for (BluetoothGattService brokenRef : includedServices) { 437 BluetoothGattService includedService = 438 getService( 439 mDevice, 440 brokenRef.getUuid(), 441 brokenRef.getInstanceId()); 442 if (includedService != null) { 443 fixedService.addIncludedService(includedService); 444 } else { 445 Log.e(TAG, "Broken GATT database: can't find included service."); 446 } 447 } 448 } 449 450 runOrQueueCallback( 451 new Runnable() { 452 @Override 453 public void run() { 454 final BluetoothGattCallback callback = mCallback; 455 if (callback != null) { 456 callback.onServicesDiscovered(BluetoothGatt.this, status); 457 } 458 } 459 }); 460 } 461 462 /** 463 * Remote characteristic has been read. Updates the internal value. 464 * 465 * @hide 466 */ 467 @Override 468 @SuppressLint("AndroidFrameworkRequiresPermission") 469 public void onCharacteristicRead( 470 String address, int status, int handle, byte[] value) { 471 if (VDBG) { 472 Log.d( 473 TAG, 474 "onCharacteristicRead() -" 475 + (" Device=" + address) 476 + (" handle=" + handle) 477 + (" Status=" + status)); 478 } 479 480 if (!address.equals(mDevice.getAddress())) { 481 return; 482 } 483 484 synchronized (mDeviceBusyLock) { 485 mDeviceBusy = false; 486 } 487 488 if ((status == GATT_INSUFFICIENT_AUTHENTICATION 489 || status == GATT_INSUFFICIENT_ENCRYPTION) 490 && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) { 491 try { 492 final int authReq = 493 (mAuthRetryState == AUTH_RETRY_STATE_IDLE) 494 ? AUTHENTICATION_NO_MITM 495 : AUTHENTICATION_MITM; 496 mService.readCharacteristic( 497 mClientIf, address, handle, authReq, mAttributionSource); 498 mAuthRetryState++; 499 return; 500 } catch (RemoteException e) { 501 Log.e(TAG, "", e); 502 } 503 } 504 505 mAuthRetryState = AUTH_RETRY_STATE_IDLE; 506 507 BluetoothGattCharacteristic characteristic = 508 getCharacteristicById(mDevice, handle); 509 if (characteristic == null) { 510 Log.w(TAG, "onCharacteristicRead() failed to find characteristic!"); 511 return; 512 } 513 514 runOrQueueCallback( 515 new Runnable() { 516 @Override 517 public void run() { 518 final BluetoothGattCallback callback = mCallback; 519 if (callback != null) { 520 if (status == 0) characteristic.setValue(value); 521 callback.onCharacteristicRead( 522 BluetoothGatt.this, characteristic, value, status); 523 } 524 } 525 }); 526 } 527 528 /** 529 * Characteristic has been written to the remote device. Let the app know how we 530 * did... 531 * 532 * @hide 533 */ 534 @Override 535 @SuppressLint("AndroidFrameworkRequiresPermission") 536 public void onCharacteristicWrite( 537 String address, int status, int handle, byte[] value) { 538 if (VDBG) { 539 Log.d( 540 TAG, 541 "onCharacteristicWrite() -" 542 + (" Device=" + address) 543 + (" handle=" + handle) 544 + (" Status=" + status)); 545 } 546 547 if (!address.equals(mDevice.getAddress())) { 548 return; 549 } 550 551 synchronized (mDeviceBusyLock) { 552 mDeviceBusy = false; 553 } 554 555 BluetoothGattCharacteristic characteristic = 556 getCharacteristicById(mDevice, handle); 557 if (characteristic == null) return; 558 559 if ((status == GATT_INSUFFICIENT_AUTHENTICATION 560 || status == GATT_INSUFFICIENT_ENCRYPTION) 561 && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) { 562 try { 563 final int authReq = 564 (mAuthRetryState == AUTH_RETRY_STATE_IDLE) 565 ? AUTHENTICATION_NO_MITM 566 : AUTHENTICATION_MITM; 567 int requestStatus = BluetoothStatusCodes.ERROR_UNKNOWN; 568 for (int i = 0; i < WRITE_CHARACTERISTIC_MAX_RETRIES; i++) { 569 requestStatus = 570 mService.writeCharacteristic( 571 mClientIf, 572 address, 573 handle, 574 characteristic.getWriteType(), 575 authReq, 576 value, 577 mAttributionSource); 578 if (requestStatus 579 != BluetoothStatusCodes.ERROR_GATT_WRITE_REQUEST_BUSY) { 580 break; 581 } 582 try { 583 Thread.sleep(WRITE_CHARACTERISTIC_TIME_TO_WAIT); 584 } catch (InterruptedException e) { 585 Log.e(TAG, "", e); 586 } 587 } 588 mAuthRetryState++; 589 return; 590 } catch (RemoteException e) { 591 Log.e(TAG, "", e); 592 } 593 } 594 595 mAuthRetryState = AUTH_RETRY_STATE_IDLE; 596 runOrQueueCallback( 597 new Runnable() { 598 @Override 599 public void run() { 600 final BluetoothGattCallback callback = mCallback; 601 if (callback != null) { 602 callback.onCharacteristicWrite( 603 BluetoothGatt.this, characteristic, status); 604 } 605 } 606 }); 607 } 608 609 /** 610 * Remote characteristic has been updated. Updates the internal value. 611 * 612 * @hide 613 */ 614 @Override 615 public void onNotify(String address, int handle, byte[] value) { 616 if (VDBG) Log.d(TAG, "onNotify() - Device=" + address + " handle=" + handle); 617 618 if (!address.equals(mDevice.getAddress())) { 619 return; 620 } 621 622 BluetoothGattCharacteristic characteristic = 623 getCharacteristicById(mDevice, handle); 624 if (characteristic == null) return; 625 626 runOrQueueCallback( 627 new Runnable() { 628 @Override 629 public void run() { 630 final BluetoothGattCallback callback = mCallback; 631 if (callback != null) { 632 characteristic.setValue(value); 633 callback.onCharacteristicChanged( 634 BluetoothGatt.this, characteristic, value); 635 } 636 } 637 }); 638 } 639 640 /** 641 * Descriptor has been read. 642 * 643 * @hide 644 */ 645 @Override 646 @SuppressLint("AndroidFrameworkRequiresPermission") 647 public void onDescriptorRead(String address, int status, int handle, byte[] value) { 648 if (VDBG) { 649 Log.d(TAG, "onDescriptorRead() - Device=" + address + " handle=" + handle); 650 } 651 652 if (!address.equals(mDevice.getAddress())) { 653 return; 654 } 655 656 synchronized (mDeviceBusyLock) { 657 mDeviceBusy = false; 658 } 659 660 BluetoothGattDescriptor descriptor = getDescriptorById(mDevice, handle); 661 if (descriptor == null) return; 662 663 if ((status == GATT_INSUFFICIENT_AUTHENTICATION 664 || status == GATT_INSUFFICIENT_ENCRYPTION) 665 && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) { 666 try { 667 final int authReq = 668 (mAuthRetryState == AUTH_RETRY_STATE_IDLE) 669 ? AUTHENTICATION_NO_MITM 670 : AUTHENTICATION_MITM; 671 mService.readDescriptor( 672 mClientIf, address, handle, authReq, mAttributionSource); 673 mAuthRetryState++; 674 return; 675 } catch (RemoteException e) { 676 Log.e(TAG, "", e); 677 } 678 } 679 680 mAuthRetryState = AUTH_RETRY_STATE_IDLE; 681 682 runOrQueueCallback( 683 new Runnable() { 684 @Override 685 public void run() { 686 final BluetoothGattCallback callback = mCallback; 687 if (callback != null) { 688 if (status == 0) descriptor.setValue(value); 689 callback.onDescriptorRead( 690 BluetoothGatt.this, descriptor, status, value); 691 } 692 } 693 }); 694 } 695 696 /** 697 * Descriptor write operation complete. 698 * 699 * @hide 700 */ 701 @Override 702 @SuppressLint("AndroidFrameworkRequiresPermission") 703 public void onDescriptorWrite( 704 String address, int status, int handle, byte[] value) { 705 if (VDBG) { 706 Log.d(TAG, "onDescriptorWrite() - Device=" + address + " handle=" + handle); 707 } 708 709 if (!address.equals(mDevice.getAddress())) { 710 return; 711 } 712 713 synchronized (mDeviceBusyLock) { 714 mDeviceBusy = false; 715 } 716 717 BluetoothGattDescriptor descriptor = getDescriptorById(mDevice, handle); 718 if (descriptor == null) return; 719 720 if ((status == GATT_INSUFFICIENT_AUTHENTICATION 721 || status == GATT_INSUFFICIENT_ENCRYPTION) 722 && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) { 723 try { 724 final int authReq = 725 (mAuthRetryState == AUTH_RETRY_STATE_IDLE) 726 ? AUTHENTICATION_NO_MITM 727 : AUTHENTICATION_MITM; 728 mService.writeDescriptor( 729 mClientIf, address, handle, authReq, value, mAttributionSource); 730 mAuthRetryState++; 731 return; 732 } catch (RemoteException e) { 733 Log.e(TAG, "", e); 734 } 735 } 736 737 mAuthRetryState = AUTH_RETRY_STATE_IDLE; 738 739 runOrQueueCallback( 740 new Runnable() { 741 @Override 742 public void run() { 743 final BluetoothGattCallback callback = mCallback; 744 if (callback != null) { 745 callback.onDescriptorWrite( 746 BluetoothGatt.this, descriptor, status); 747 } 748 } 749 }); 750 } 751 752 /** 753 * Prepared write transaction completed (or aborted) 754 * 755 * @hide 756 */ 757 @Override 758 public void onExecuteWrite(String address, int status) { 759 if (VDBG) { 760 Log.d(TAG, "onExecuteWrite() - Device=" + address + " status=" + status); 761 } 762 if (!address.equals(mDevice.getAddress())) { 763 return; 764 } 765 766 synchronized (mDeviceBusyLock) { 767 mDeviceBusy = false; 768 } 769 770 runOrQueueCallback( 771 new Runnable() { 772 @Override 773 public void run() { 774 final BluetoothGattCallback callback = mCallback; 775 if (callback != null) { 776 callback.onReliableWriteCompleted( 777 BluetoothGatt.this, status); 778 } 779 } 780 }); 781 } 782 783 /** 784 * Remote device RSSI has been read 785 * 786 * @hide 787 */ 788 @Override 789 public void onReadRemoteRssi(String address, int rssi, int status) { 790 if (VDBG) { 791 Log.d( 792 TAG, 793 "onReadRemoteRssi() -" 794 + (" Device=" + address) 795 + (" rssi=" + rssi) 796 + (" status=" + status)); 797 } 798 if (!address.equals(mDevice.getAddress())) { 799 return; 800 } 801 runOrQueueCallback( 802 new Runnable() { 803 @Override 804 public void run() { 805 final BluetoothGattCallback callback = mCallback; 806 if (callback != null) { 807 callback.onReadRemoteRssi(BluetoothGatt.this, rssi, status); 808 } 809 } 810 }); 811 } 812 813 /** 814 * Callback invoked when the MTU for a given connection changes 815 * 816 * @hide 817 */ 818 @Override 819 public void onConfigureMTU(String address, int mtu, int status) { 820 if (DBG) { 821 Log.d( 822 TAG, 823 "onConfigureMTU() -" 824 + (" Device=" + address) 825 + (" mtu=" + mtu) 826 + (" status=" + status)); 827 } 828 if (!address.equals(mDevice.getAddress())) { 829 return; 830 } 831 832 runOrQueueCallback( 833 new Runnable() { 834 @Override 835 public void run() { 836 final BluetoothGattCallback callback = mCallback; 837 if (callback != null) { 838 callback.onMtuChanged(BluetoothGatt.this, mtu, status); 839 } 840 } 841 }); 842 } 843 844 /** 845 * Callback invoked when the given connection is updated 846 * 847 * @hide 848 */ 849 @Override 850 public void onConnectionUpdated( 851 String address, int interval, int latency, int timeout, int status) { 852 if (DBG) { 853 Log.d( 854 TAG, 855 "onConnectionUpdated() -" 856 + (" Device=" + address) 857 + (" interval=" + interval) 858 + (" latency=" + latency) 859 + (" timeout=" + timeout) 860 + (" status=" + status)); 861 } 862 if (!address.equals(mDevice.getAddress())) { 863 return; 864 } 865 866 runOrQueueCallback( 867 new Runnable() { 868 @Override 869 public void run() { 870 final BluetoothGattCallback callback = mCallback; 871 if (callback != null) { 872 callback.onConnectionUpdated( 873 BluetoothGatt.this, 874 interval, 875 latency, 876 timeout, 877 status); 878 } 879 } 880 }); 881 } 882 883 /** 884 * Callback invoked when service changed event is received 885 * 886 * @hide 887 */ 888 @Override 889 public void onServiceChanged(String address) { 890 if (DBG) { 891 Log.d(TAG, "onServiceChanged() - Device=" + address); 892 } 893 894 if (!address.equals(mDevice.getAddress())) { 895 return; 896 } 897 898 runOrQueueCallback( 899 new Runnable() { 900 @Override 901 public void run() { 902 final BluetoothGattCallback callback = mCallback; 903 if (callback != null) { 904 callback.onServiceChanged(BluetoothGatt.this); 905 } 906 } 907 }); 908 } 909 910 /** 911 * Callback invoked when the given connection's subrate is changed 912 * 913 * @hide 914 */ 915 @Override 916 public void onSubrateChange( 917 String address, 918 int subrateFactor, 919 int latency, 920 int contNum, 921 int timeout, 922 int status) { 923 Log.d( 924 TAG, 925 "onSubrateChange() - " 926 + (" Device=" + address) 927 + (" subrateFactor=" + subrateFactor) 928 + (" latency=" + latency) 929 + (" contNum=" + contNum) 930 + (" timeout=" + timeout) 931 + (" status=" + status)); 932 933 if (!address.equals(mDevice.getAddress())) { 934 return; 935 } 936 937 runOrQueueCallback( 938 new Runnable() { 939 @Override 940 public void run() { 941 final BluetoothGattCallback callback = mCallback; 942 if (callback != null) { 943 callback.onSubrateChange( 944 BluetoothGatt.this, 945 subrateFactor, 946 latency, 947 contNum, 948 timeout, 949 status); 950 } 951 } 952 }); 953 } 954 }; 955 BluetoothGatt( IBluetoothGatt iGatt, BluetoothDevice device, int transport, boolean opportunistic, int phy, AttributionSource attributionSource)956 /* package */ BluetoothGatt( 957 IBluetoothGatt iGatt, 958 BluetoothDevice device, 959 int transport, 960 boolean opportunistic, 961 int phy, 962 AttributionSource attributionSource) { 963 mService = iGatt; 964 mDevice = device; 965 mTransport = transport; 966 mPhy = phy; 967 mOpportunistic = opportunistic; 968 mAttributionSource = attributionSource; 969 mServices = new ArrayList<BluetoothGattService>(); 970 971 mConnState = CONN_STATE_IDLE; 972 mAuthRetryState = AUTH_RETRY_STATE_IDLE; 973 } 974 975 /** @hide */ 976 @Override onServiceConnected(IBinder service)977 public void onServiceConnected(IBinder service) {} 978 979 /** @hide */ 980 @Override onServiceDisconnected()981 public void onServiceDisconnected() {} 982 983 /** @hide */ 984 @Override getAdapter()985 public BluetoothAdapter getAdapter() { 986 return null; 987 } 988 989 /** 990 * Close this Bluetooth GATT client. 991 * 992 * <p>Application should call this method as early as possible after it is done with this GATT 993 * client. 994 */ 995 @RequiresBluetoothConnectPermission 996 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) close()997 public void close() { 998 if (DBG) Log.d(TAG, "close()"); 999 1000 unregisterApp(); 1001 mConnState = CONN_STATE_CLOSED; 1002 mAuthRetryState = AUTH_RETRY_STATE_IDLE; 1003 } 1004 1005 /** 1006 * Returns a service by UUID, instance and type. 1007 * 1008 * @hide 1009 */ getService(BluetoothDevice device, UUID uuid, int instanceId)1010 /*package*/ BluetoothGattService getService(BluetoothDevice device, UUID uuid, int instanceId) { 1011 for (BluetoothGattService svc : mServices) { 1012 if (svc.getDevice().equals(device) 1013 && svc.getInstanceId() == instanceId 1014 && svc.getUuid().equals(uuid)) { 1015 return svc; 1016 } 1017 } 1018 return null; 1019 } 1020 1021 /** 1022 * Returns a characteristic with id equal to instanceId. 1023 * 1024 * @hide 1025 */ getCharacteristicById( BluetoothDevice device, int instanceId)1026 /*package*/ BluetoothGattCharacteristic getCharacteristicById( 1027 BluetoothDevice device, int instanceId) { 1028 for (BluetoothGattService svc : mServices) { 1029 for (BluetoothGattCharacteristic charac : svc.getCharacteristics()) { 1030 if (charac.getInstanceId() == instanceId) { 1031 return charac; 1032 } 1033 } 1034 } 1035 return null; 1036 } 1037 1038 /** 1039 * Returns a descriptor with id equal to instanceId. 1040 * 1041 * @hide 1042 */ getDescriptorById(BluetoothDevice device, int instanceId)1043 /*package*/ BluetoothGattDescriptor getDescriptorById(BluetoothDevice device, int instanceId) { 1044 for (BluetoothGattService svc : mServices) { 1045 for (BluetoothGattCharacteristic charac : svc.getCharacteristics()) { 1046 for (BluetoothGattDescriptor desc : charac.getDescriptors()) { 1047 if (desc.getInstanceId() == instanceId) { 1048 return desc; 1049 } 1050 } 1051 } 1052 } 1053 return null; 1054 } 1055 1056 /** 1057 * Queue the runnable on a {@link Handler} provided by the user, or execute the runnable 1058 * immediately if no Handler was provided. 1059 */ runOrQueueCallback(final Runnable cb)1060 private void runOrQueueCallback(final Runnable cb) { 1061 if (mHandler == null) { 1062 try { 1063 cb.run(); 1064 } catch (Exception ex) { 1065 Log.w(TAG, "Unhandled exception in callback", ex); 1066 } 1067 } else { 1068 mHandler.post(cb); 1069 } 1070 } 1071 1072 /** 1073 * Register an application callback to start using GATT. 1074 * 1075 * <p>This is an asynchronous call. If registration is successful, client connection will be 1076 * initiated. 1077 * 1078 * @param callback GATT callback handler that will receive asynchronous callbacks. 1079 * @return If true, the callback will be called to notify success or failure, false on immediate 1080 * error 1081 */ 1082 @RequiresLegacyBluetoothPermission 1083 @RequiresBluetoothConnectPermission 1084 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) registerApp(BluetoothGattCallback callback, Handler handler)1085 private boolean registerApp(BluetoothGattCallback callback, Handler handler) { 1086 return registerApp(callback, handler, false); 1087 } 1088 1089 /** 1090 * Register an application callback to start using GATT. 1091 * 1092 * <p>This is an asynchronous call. If registration is successful, client connection will be 1093 * initiated. 1094 * 1095 * @param callback GATT callback handler that will receive asynchronous callbacks. 1096 * @param eattSupport indicate to allow for eatt support 1097 * @return If true, the callback will be called to notify success or failure, false on immediate 1098 * error 1099 * @hide 1100 */ 1101 @RequiresLegacyBluetoothPermission 1102 @RequiresBluetoothConnectPermission 1103 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) registerApp( BluetoothGattCallback callback, Handler handler, boolean eattSupport)1104 private boolean registerApp( 1105 BluetoothGattCallback callback, Handler handler, boolean eattSupport) { 1106 if (DBG) Log.d(TAG, "registerApp()"); 1107 if (mService == null) return false; 1108 1109 mCallback = callback; 1110 mHandler = handler; 1111 UUID uuid = UUID.randomUUID(); 1112 if (DBG) Log.d(TAG, "registerApp() - UUID=" + uuid); 1113 1114 try { 1115 mService.registerClient( 1116 new ParcelUuid(uuid), mBluetoothGattCallback, eattSupport, mAttributionSource); 1117 } catch (RemoteException e) { 1118 Log.e(TAG, "", e); 1119 return false; 1120 } 1121 1122 return true; 1123 } 1124 1125 /** Unregister the current application and callbacks. */ 1126 @UnsupportedAppUsage 1127 @RequiresBluetoothConnectPermission 1128 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) unregisterApp()1129 private void unregisterApp() { 1130 if (mService == null || mClientIf == 0) return; 1131 if (DBG) Log.d(TAG, "unregisterApp() - mClientIf=" + mClientIf); 1132 1133 try { 1134 mCallback = null; 1135 mService.unregisterClient(mClientIf, mAttributionSource); 1136 mClientIf = 0; 1137 } catch (RemoteException e) { 1138 Log.e(TAG, "", e); 1139 } 1140 } 1141 1142 /** 1143 * Initiate a connection to a Bluetooth GATT capable device. 1144 * 1145 * <p>The connection may not be established right away, but will be completed when the remote 1146 * device is available. A {@link BluetoothGattCallback#onConnectionStateChange} callback will be 1147 * invoked when the connection state changes as a result of this function. 1148 * 1149 * <p>The autoConnect parameter determines whether to actively connect to the remote device, or 1150 * rather passively scan and finalize the connection when the remote device is in 1151 * range/available. Generally, the first ever connection to a device should be direct 1152 * (autoConnect set to false) and subsequent connections to known devices should be invoked with 1153 * the autoConnect parameter set to true. 1154 * 1155 * @param autoConnect Whether to directly connect to the remote device (false) or to 1156 * automatically connect as soon as the remote device becomes available (true). 1157 * @return true, if the connection attempt was initiated successfully 1158 */ 1159 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 1160 @RequiresLegacyBluetoothPermission 1161 @RequiresBluetoothConnectPermission 1162 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) connect( Boolean autoConnect, BluetoothGattCallback callback, Handler handler)1163 /*package*/ boolean connect( 1164 Boolean autoConnect, BluetoothGattCallback callback, Handler handler) { 1165 if (DBG) { 1166 Log.d(TAG, "connect() - device: " + mDevice + ", auto: " + autoConnect); 1167 } 1168 synchronized (mStateLock) { 1169 if (mConnState != CONN_STATE_IDLE) { 1170 throw new IllegalStateException("Not idle"); 1171 } 1172 mConnState = CONN_STATE_CONNECTING; 1173 } 1174 1175 mAutoConnect = autoConnect; 1176 1177 if (!registerApp(callback, handler)) { 1178 synchronized (mStateLock) { 1179 mConnState = CONN_STATE_IDLE; 1180 } 1181 Log.e(TAG, "Failed to register callback"); 1182 return false; 1183 } 1184 1185 // The connection will continue in the onClientRegistered callback 1186 return true; 1187 } 1188 1189 /** 1190 * Disconnects an established connection, or cancels a connection attempt currently in progress. 1191 */ 1192 @RequiresLegacyBluetoothPermission 1193 @RequiresBluetoothConnectPermission 1194 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) disconnect()1195 public void disconnect() { 1196 if (DBG) Log.d(TAG, "cancelOpen() - device: " + mDevice); 1197 if (mService == null || mClientIf == 0) return; 1198 1199 try { 1200 mService.clientDisconnect(mClientIf, mDevice.getAddress(), mAttributionSource); 1201 } catch (RemoteException e) { 1202 Log.e(TAG, "", e); 1203 } 1204 } 1205 1206 /** 1207 * Connect back to remote device. 1208 * 1209 * <p>This method is used to re-connect to a remote device after the connection has been 1210 * dropped. If the device is not in range, the re-connection will be triggered once the device 1211 * is back in range. 1212 * 1213 * @return true, if the connection attempt was initiated successfully 1214 */ 1215 @RequiresBluetoothConnectPermission 1216 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) connect()1217 public boolean connect() { 1218 try { 1219 if (DBG) { 1220 Log.d(TAG, "connect(void) - device: " + mDevice + ", auto=" + mAutoConnect); 1221 } 1222 1223 // autoConnect is inverse of "isDirect" 1224 mService.clientConnect( 1225 mClientIf, 1226 mDevice.getAddress(), 1227 mDevice.getAddressType(), 1228 !mAutoConnect, 1229 mTransport, 1230 mOpportunistic, 1231 mPhy, 1232 mAttributionSource); 1233 return true; 1234 } catch (RemoteException e) { 1235 Log.e(TAG, "", e); 1236 return false; 1237 } 1238 } 1239 1240 /** 1241 * Set the preferred connection PHY for this app. Please note that this is just a 1242 * recommendation, whether the PHY change will happen depends on other applications preferences, 1243 * local and remote controller capabilities. Controller can override these settings. 1244 * 1245 * <p>{@link BluetoothGattCallback#onPhyUpdate} will be triggered as a result of this call, even 1246 * if no PHY change happens. It is also triggered when remote device updates the PHY. 1247 * 1248 * @param txPhy preferred transmitter PHY. Bitwise OR of any of {@link 1249 * BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, and {@link 1250 * BluetoothDevice#PHY_LE_CODED_MASK}. 1251 * @param rxPhy preferred receiver PHY. Bitwise OR of any of {@link 1252 * BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, and {@link 1253 * BluetoothDevice#PHY_LE_CODED_MASK}. 1254 * @param phyOptions preferred coding to use when transmitting on the LE Coded PHY. Can be one 1255 * of {@link BluetoothDevice#PHY_OPTION_NO_PREFERRED}, {@link BluetoothDevice#PHY_OPTION_S2} 1256 * or {@link BluetoothDevice#PHY_OPTION_S8} 1257 */ 1258 @RequiresBluetoothConnectPermission 1259 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) setPreferredPhy(int txPhy, int rxPhy, int phyOptions)1260 public void setPreferredPhy(int txPhy, int rxPhy, int phyOptions) { 1261 try { 1262 mService.clientSetPreferredPhy( 1263 mClientIf, mDevice.getAddress(), txPhy, rxPhy, phyOptions, mAttributionSource); 1264 } catch (RemoteException e) { 1265 Log.e(TAG, "", e); 1266 } 1267 } 1268 1269 /** 1270 * Read the current transmitter PHY and receiver PHY of the connection. The values are returned 1271 * in {@link BluetoothGattCallback#onPhyRead} 1272 */ 1273 @RequiresBluetoothConnectPermission 1274 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) readPhy()1275 public void readPhy() { 1276 try { 1277 mService.clientReadPhy(mClientIf, mDevice.getAddress(), mAttributionSource); 1278 } catch (RemoteException e) { 1279 Log.e(TAG, "", e); 1280 } 1281 } 1282 1283 /** 1284 * Return the remote bluetooth device this GATT client targets to 1285 * 1286 * @return remote bluetooth device 1287 */ 1288 @RequiresNoPermission getDevice()1289 public BluetoothDevice getDevice() { 1290 return mDevice; 1291 } 1292 1293 /** 1294 * Discovers services offered by a remote device as well as their characteristics and 1295 * descriptors. 1296 * 1297 * <p>This is an asynchronous operation. Once service discovery is completed, the {@link 1298 * BluetoothGattCallback#onServicesDiscovered} callback is triggered. If the discovery was 1299 * successful, the remote services can be retrieved using the {@link #getServices} function. 1300 * 1301 * @return true, if the remote service discovery has been started 1302 */ 1303 @RequiresLegacyBluetoothPermission 1304 @RequiresBluetoothConnectPermission 1305 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) discoverServices()1306 public boolean discoverServices() { 1307 if (DBG) Log.d(TAG, "discoverServices() - device: " + mDevice); 1308 if (mService == null || mClientIf == 0) return false; 1309 1310 mServices.clear(); 1311 1312 try { 1313 mService.discoverServices(mClientIf, mDevice.getAddress(), mAttributionSource); 1314 } catch (RemoteException e) { 1315 Log.e(TAG, "", e); 1316 return false; 1317 } 1318 1319 return true; 1320 } 1321 1322 /** 1323 * Discovers a service by UUID. This is exposed only for passing PTS tests. It should never be 1324 * used by real applications. The service is not searched for characteristics and descriptors, 1325 * or returned in any callback. 1326 * 1327 * @return true, if the remote service discovery has been started 1328 * @hide 1329 */ 1330 @RequiresLegacyBluetoothPermission 1331 @RequiresBluetoothConnectPermission 1332 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) discoverServiceByUuid(UUID uuid)1333 public boolean discoverServiceByUuid(UUID uuid) { 1334 if (DBG) Log.d(TAG, "discoverServiceByUuid() - device: " + mDevice); 1335 if (mService == null || mClientIf == 0) return false; 1336 1337 mServices.clear(); 1338 1339 try { 1340 mService.discoverServiceByUuid( 1341 mClientIf, mDevice.getAddress(), new ParcelUuid(uuid), mAttributionSource); 1342 } catch (RemoteException e) { 1343 Log.e(TAG, "", e); 1344 return false; 1345 } 1346 return true; 1347 } 1348 1349 /** 1350 * Returns a list of GATT services offered by the remote device. 1351 * 1352 * <p>This function requires that service discovery has been completed for the given device. 1353 * 1354 * @return List of services on the remote device. Returns an empty list if service discovery has 1355 * not yet been performed. 1356 */ 1357 @RequiresLegacyBluetoothPermission 1358 @RequiresNoPermission getServices()1359 public List<BluetoothGattService> getServices() { 1360 List<BluetoothGattService> result = new ArrayList<BluetoothGattService>(); 1361 1362 for (BluetoothGattService service : mServices) { 1363 if (service.getDevice().equals(mDevice)) { 1364 result.add(service); 1365 } 1366 } 1367 1368 return result; 1369 } 1370 1371 /** 1372 * Returns a {@link BluetoothGattService}, if the requested UUID is supported by the remote 1373 * device. 1374 * 1375 * <p>This function requires that service discovery has been completed for the given device. 1376 * 1377 * <p>If multiple instances of the same service (as identified by UUID) exist, the first 1378 * instance of the service is returned. 1379 * 1380 * @param uuid UUID of the requested service 1381 * @return BluetoothGattService if supported, or null if the requested service is not offered by 1382 * the remote device. 1383 */ 1384 @RequiresLegacyBluetoothPermission 1385 @RequiresNoPermission getService(UUID uuid)1386 public BluetoothGattService getService(UUID uuid) { 1387 for (BluetoothGattService service : mServices) { 1388 if (service.getDevice().equals(mDevice) && service.getUuid().equals(uuid)) { 1389 return service; 1390 } 1391 } 1392 1393 return null; 1394 } 1395 1396 /** 1397 * Reads the requested characteristic from the associated remote device. 1398 * 1399 * <p>This is an asynchronous operation. The result of the read operation is reported by the 1400 * {@link BluetoothGattCallback#onCharacteristicRead(BluetoothGatt, BluetoothGattCharacteristic, 1401 * byte[], int)} callback. 1402 * 1403 * @param characteristic Characteristic to read from the remote device 1404 * @return true, if the read operation was initiated successfully 1405 */ 1406 @RequiresLegacyBluetoothPermission 1407 @RequiresBluetoothConnectPermission 1408 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) readCharacteristic(BluetoothGattCharacteristic characteristic)1409 public boolean readCharacteristic(BluetoothGattCharacteristic characteristic) { 1410 if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_READ) == 0) { 1411 return false; 1412 } 1413 1414 if (VDBG) Log.d(TAG, "readCharacteristic() - uuid: " + characteristic.getUuid()); 1415 if (mService == null || mClientIf == 0) return false; 1416 1417 BluetoothGattService service = characteristic.getService(); 1418 if (service == null) return false; 1419 1420 BluetoothDevice device = service.getDevice(); 1421 if (device == null) return false; 1422 1423 synchronized (mDeviceBusyLock) { 1424 if (mDeviceBusy) return false; 1425 mDeviceBusy = true; 1426 } 1427 1428 try { 1429 mService.readCharacteristic( 1430 mClientIf, 1431 device.getAddress(), 1432 characteristic.getInstanceId(), 1433 AUTHENTICATION_NONE, 1434 mAttributionSource); 1435 } catch (RemoteException e) { 1436 Log.e(TAG, "", e); 1437 synchronized (mDeviceBusyLock) { 1438 mDeviceBusy = false; 1439 } 1440 return false; 1441 } 1442 1443 return true; 1444 } 1445 1446 /** 1447 * Reads the characteristic using its UUID from the associated remote device. 1448 * 1449 * <p>This is an asynchronous operation. The result of the read operation is reported by the 1450 * {@link BluetoothGattCallback#onCharacteristicRead(BluetoothGatt, BluetoothGattCharacteristic, 1451 * byte[], int)} callback. 1452 * 1453 * @param uuid UUID of characteristic to read from the remote device 1454 * @return true, if the read operation was initiated successfully 1455 * @hide 1456 */ 1457 @RequiresLegacyBluetoothPermission 1458 @RequiresBluetoothConnectPermission 1459 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) readUsingCharacteristicUuid(UUID uuid, int startHandle, int endHandle)1460 public boolean readUsingCharacteristicUuid(UUID uuid, int startHandle, int endHandle) { 1461 if (VDBG) Log.d(TAG, "readUsingCharacteristicUuid() - uuid: " + uuid); 1462 if (mService == null || mClientIf == 0) return false; 1463 1464 synchronized (mDeviceBusyLock) { 1465 if (mDeviceBusy) return false; 1466 mDeviceBusy = true; 1467 } 1468 1469 try { 1470 mService.readUsingCharacteristicUuid( 1471 mClientIf, 1472 mDevice.getAddress(), 1473 new ParcelUuid(uuid), 1474 startHandle, 1475 endHandle, 1476 AUTHENTICATION_NONE, 1477 mAttributionSource); 1478 } catch (RemoteException e) { 1479 Log.e(TAG, "", e); 1480 synchronized (mDeviceBusyLock) { 1481 mDeviceBusy = false; 1482 } 1483 return false; 1484 } 1485 1486 return true; 1487 } 1488 1489 /** 1490 * Writes a given characteristic and its values to the associated remote device. 1491 * 1492 * <p>Once the write operation has been completed, the {@link 1493 * BluetoothGattCallback#onCharacteristicWrite} callback is invoked, reporting the result of the 1494 * operation. 1495 * 1496 * @param characteristic Characteristic to write on the remote device 1497 * @return true, if the write operation was initiated successfully 1498 * @throws IllegalArgumentException if characteristic or its value are null 1499 * @deprecated Use {@link BluetoothGatt#writeCharacteristic(BluetoothGattCharacteristic, byte[], 1500 * int)} as this is not memory safe because it relies on a {@link 1501 * BluetoothGattCharacteristic} object whose underlying fields are subject to change outside 1502 * this method. 1503 */ 1504 @Deprecated 1505 @RequiresLegacyBluetoothPermission 1506 @RequiresBluetoothConnectPermission 1507 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) writeCharacteristic(BluetoothGattCharacteristic characteristic)1508 public boolean writeCharacteristic(BluetoothGattCharacteristic characteristic) { 1509 try { 1510 return writeCharacteristic( 1511 characteristic, 1512 characteristic.getValue(), 1513 characteristic.getWriteType()) 1514 == BluetoothStatusCodes.SUCCESS; 1515 } catch (Exception e) { 1516 return false; 1517 } 1518 } 1519 1520 /** @hide */ 1521 @Retention(RetentionPolicy.SOURCE) 1522 @IntDef( 1523 value = { 1524 BluetoothStatusCodes.SUCCESS, 1525 BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION, 1526 BluetoothStatusCodes.ERROR_DEVICE_NOT_CONNECTED, 1527 BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND, 1528 BluetoothStatusCodes.ERROR_GATT_WRITE_NOT_ALLOWED, 1529 BluetoothStatusCodes.ERROR_GATT_WRITE_REQUEST_BUSY, 1530 BluetoothStatusCodes.ERROR_UNKNOWN 1531 }) 1532 public @interface WriteOperationReturnValues {} 1533 1534 /** 1535 * Writes a given characteristic and its values to the associated remote device. 1536 * 1537 * <p>Once the write operation has been completed, the {@link 1538 * BluetoothGattCallback#onCharacteristicWrite} callback is invoked, reporting the result of the 1539 * operation. 1540 * 1541 * @param characteristic Characteristic to write on the remote device 1542 * @return whether the characteristic was successfully written to 1543 * @throws IllegalArgumentException if characteristic or value are null 1544 */ 1545 @RequiresBluetoothConnectPermission 1546 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) 1547 @WriteOperationReturnValues writeCharacteristic( @onNull BluetoothGattCharacteristic characteristic, @NonNull byte[] value, @WriteType int writeType)1548 public int writeCharacteristic( 1549 @NonNull BluetoothGattCharacteristic characteristic, 1550 @NonNull byte[] value, 1551 @WriteType int writeType) { 1552 if (characteristic == null) { 1553 throw new IllegalArgumentException("characteristic must not be null"); 1554 } 1555 if (value == null) { 1556 throw new IllegalArgumentException("value must not be null"); 1557 } 1558 if (value.length > GATT_MAX_ATTR_LEN) { 1559 throw new IllegalArgumentException( 1560 "value should not be longer than max length of an attribute value"); 1561 } 1562 if (VDBG) Log.d(TAG, "writeCharacteristic() - uuid: " + characteristic.getUuid()); 1563 if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) == 0 1564 && (characteristic.getProperties() 1565 & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) 1566 == 0) { 1567 return BluetoothStatusCodes.ERROR_GATT_WRITE_NOT_ALLOWED; 1568 } 1569 if (mService == null || mClientIf == 0) { 1570 return BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND; 1571 } 1572 1573 BluetoothGattService service = characteristic.getService(); 1574 if (service == null) { 1575 throw new IllegalArgumentException("Characteristic must have a non-null service"); 1576 } 1577 1578 BluetoothDevice device = service.getDevice(); 1579 if (device == null) { 1580 throw new IllegalArgumentException("Service must have a non-null device"); 1581 } 1582 1583 synchronized (mDeviceBusyLock) { 1584 if (mDeviceBusy) { 1585 return BluetoothStatusCodes.ERROR_GATT_WRITE_REQUEST_BUSY; 1586 } 1587 mDeviceBusy = true; 1588 } 1589 1590 int requestStatus = BluetoothStatusCodes.ERROR_UNKNOWN; 1591 try { 1592 for (int i = 0; i < WRITE_CHARACTERISTIC_MAX_RETRIES; i++) { 1593 requestStatus = 1594 mService.writeCharacteristic( 1595 mClientIf, 1596 device.getAddress(), 1597 characteristic.getInstanceId(), 1598 writeType, 1599 AUTHENTICATION_NONE, 1600 value, 1601 mAttributionSource); 1602 if (requestStatus != BluetoothStatusCodes.ERROR_GATT_WRITE_REQUEST_BUSY) { 1603 break; 1604 } 1605 try { 1606 Thread.sleep(WRITE_CHARACTERISTIC_TIME_TO_WAIT); 1607 } catch (InterruptedException e) { 1608 Log.e(TAG, "", e); 1609 } 1610 } 1611 } catch (RemoteException e) { 1612 Log.e(TAG, "", e); 1613 synchronized (mDeviceBusyLock) { 1614 mDeviceBusy = false; 1615 } 1616 throw e.rethrowAsRuntimeException(); 1617 } 1618 if (Flags.gattFixDeviceBusy()) { 1619 if (requestStatus != BluetoothStatusCodes.SUCCESS) { 1620 synchronized (mDeviceBusyLock) { 1621 mDeviceBusy = false; 1622 } 1623 } 1624 } 1625 1626 return requestStatus; 1627 } 1628 1629 /** 1630 * Reads the value for a given descriptor from the associated remote device. 1631 * 1632 * <p>Once the read operation has been completed, the {@link 1633 * BluetoothGattCallback#onDescriptorRead} callback is triggered, signaling the result of the 1634 * operation. 1635 * 1636 * @param descriptor Descriptor value to read from the remote device 1637 * @return true, if the read operation was initiated successfully 1638 */ 1639 @RequiresLegacyBluetoothPermission 1640 @RequiresBluetoothConnectPermission 1641 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) readDescriptor(BluetoothGattDescriptor descriptor)1642 public boolean readDescriptor(BluetoothGattDescriptor descriptor) { 1643 if (VDBG) Log.d(TAG, "readDescriptor() - uuid: " + descriptor.getUuid()); 1644 if (mService == null || mClientIf == 0) return false; 1645 1646 BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic(); 1647 if (characteristic == null) return false; 1648 1649 BluetoothGattService service = characteristic.getService(); 1650 if (service == null) return false; 1651 1652 BluetoothDevice device = service.getDevice(); 1653 if (device == null) return false; 1654 1655 synchronized (mDeviceBusyLock) { 1656 if (mDeviceBusy) return false; 1657 mDeviceBusy = true; 1658 } 1659 1660 try { 1661 mService.readDescriptor( 1662 mClientIf, 1663 device.getAddress(), 1664 descriptor.getInstanceId(), 1665 AUTHENTICATION_NONE, 1666 mAttributionSource); 1667 } catch (RemoteException e) { 1668 Log.e(TAG, "", e); 1669 synchronized (mDeviceBusyLock) { 1670 mDeviceBusy = false; 1671 } 1672 return false; 1673 } 1674 1675 return true; 1676 } 1677 1678 /** 1679 * Write the value of a given descriptor to the associated remote device. 1680 * 1681 * <p>A {@link BluetoothGattCallback#onDescriptorWrite} callback is triggered to report the 1682 * result of the write operation. 1683 * 1684 * @param descriptor Descriptor to write to the associated remote device 1685 * @return true, if the write operation was initiated successfully 1686 * @throws IllegalArgumentException if descriptor or its value are null 1687 * @deprecated Use {@link BluetoothGatt#writeDescriptor(BluetoothGattDescriptor, byte[])} as 1688 * this is not memory safe because it relies on a {@link BluetoothGattDescriptor} object 1689 * whose underlying fields are subject to change outside this method. 1690 */ 1691 @Deprecated 1692 @RequiresLegacyBluetoothPermission 1693 @RequiresBluetoothConnectPermission 1694 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) writeDescriptor(BluetoothGattDescriptor descriptor)1695 public boolean writeDescriptor(BluetoothGattDescriptor descriptor) { 1696 try { 1697 return writeDescriptor(descriptor, descriptor.getValue()) 1698 == BluetoothStatusCodes.SUCCESS; 1699 } catch (Exception e) { 1700 return false; 1701 } 1702 } 1703 1704 /** 1705 * Write the value of a given descriptor to the associated remote device. 1706 * 1707 * <p>A {@link BluetoothGattCallback#onDescriptorWrite} callback is triggered to report the 1708 * result of the write operation. 1709 * 1710 * @param descriptor Descriptor to write to the associated remote device 1711 * @return true, if the write operation was initiated successfully 1712 * @throws IllegalArgumentException if descriptor or value are null 1713 */ 1714 @RequiresBluetoothConnectPermission 1715 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) 1716 @WriteOperationReturnValues writeDescriptor(@onNull BluetoothGattDescriptor descriptor, @NonNull byte[] value)1717 public int writeDescriptor(@NonNull BluetoothGattDescriptor descriptor, @NonNull byte[] value) { 1718 if (descriptor == null) { 1719 throw new IllegalArgumentException("descriptor must not be null"); 1720 } 1721 if (value == null) { 1722 throw new IllegalArgumentException("value must not be null"); 1723 } 1724 if (VDBG) Log.d(TAG, "writeDescriptor() - uuid: " + descriptor.getUuid()); 1725 if (mService == null || mClientIf == 0) { 1726 return BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND; 1727 } 1728 1729 BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic(); 1730 if (characteristic == null) { 1731 throw new IllegalArgumentException("Descriptor must have a non-null characteristic"); 1732 } 1733 1734 BluetoothGattService service = characteristic.getService(); 1735 if (service == null) { 1736 throw new IllegalArgumentException("Characteristic must have a non-null service"); 1737 } 1738 1739 BluetoothDevice device = service.getDevice(); 1740 if (device == null) { 1741 throw new IllegalArgumentException("Service must have a non-null device"); 1742 } 1743 1744 synchronized (mDeviceBusyLock) { 1745 if (mDeviceBusy) return BluetoothStatusCodes.ERROR_GATT_WRITE_REQUEST_BUSY; 1746 mDeviceBusy = true; 1747 } 1748 1749 try { 1750 return mService.writeDescriptor( 1751 mClientIf, 1752 device.getAddress(), 1753 descriptor.getInstanceId(), 1754 AUTHENTICATION_NONE, 1755 value, 1756 mAttributionSource); 1757 } catch (RemoteException e) { 1758 Log.e(TAG, "", e); 1759 synchronized (mDeviceBusyLock) { 1760 mDeviceBusy = false; 1761 } 1762 throw e.rethrowAsRuntimeException(); 1763 } 1764 } 1765 1766 /** 1767 * Initiates a reliable write transaction for a given remote device. 1768 * 1769 * <p>Once a reliable write transaction has been initiated, all calls to {@link 1770 * #writeCharacteristic} are sent to the remote device for verification and queued up for atomic 1771 * execution. The application will receive a {@link BluetoothGattCallback#onCharacteristicWrite} 1772 * callback in response to every {@link #writeCharacteristic(BluetoothGattCharacteristic, 1773 * byte[], int)} call and is responsible for verifying if the value has been transmitted 1774 * accurately. 1775 * 1776 * <p>After all characteristics have been queued up and verified, {@link #executeReliableWrite} 1777 * will execute all writes. If a characteristic was not written correctly, calling {@link 1778 * #abortReliableWrite} will cancel the current transaction without committing any values on the 1779 * remote device. 1780 * 1781 * @return true, if the reliable write transaction has been initiated 1782 */ 1783 @RequiresLegacyBluetoothPermission 1784 @RequiresBluetoothConnectPermission 1785 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) beginReliableWrite()1786 public boolean beginReliableWrite() { 1787 if (VDBG) Log.d(TAG, "beginReliableWrite() - device: " + mDevice); 1788 if (mService == null || mClientIf == 0) return false; 1789 1790 try { 1791 mService.beginReliableWrite(mClientIf, mDevice.getAddress(), mAttributionSource); 1792 } catch (RemoteException e) { 1793 Log.e(TAG, "", e); 1794 return false; 1795 } 1796 1797 return true; 1798 } 1799 1800 /** 1801 * Executes a reliable write transaction for a given remote device. 1802 * 1803 * <p>This function will commit all queued up characteristic write operations for a given remote 1804 * device. 1805 * 1806 * <p>A {@link BluetoothGattCallback#onReliableWriteCompleted} callback is invoked to indicate 1807 * whether the transaction has been executed correctly. 1808 * 1809 * @return true, if the request to execute the transaction has been sent 1810 */ 1811 @RequiresLegacyBluetoothPermission 1812 @RequiresBluetoothConnectPermission 1813 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) executeReliableWrite()1814 public boolean executeReliableWrite() { 1815 if (VDBG) Log.d(TAG, "executeReliableWrite() - device: " + mDevice); 1816 if (mService == null || mClientIf == 0) return false; 1817 1818 synchronized (mDeviceBusyLock) { 1819 if (mDeviceBusy) return false; 1820 mDeviceBusy = true; 1821 } 1822 1823 try { 1824 mService.endReliableWrite(mClientIf, mDevice.getAddress(), true, mAttributionSource); 1825 } catch (RemoteException e) { 1826 Log.e(TAG, "", e); 1827 synchronized (mDeviceBusyLock) { 1828 mDeviceBusy = false; 1829 } 1830 return false; 1831 } 1832 1833 return true; 1834 } 1835 1836 /** 1837 * Cancels a reliable write transaction for a given device. 1838 * 1839 * <p>Calling this function will discard all queued characteristic write operations for a given 1840 * remote device. 1841 */ 1842 @RequiresLegacyBluetoothPermission 1843 @RequiresBluetoothConnectPermission 1844 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) abortReliableWrite()1845 public void abortReliableWrite() { 1846 if (VDBG) Log.d(TAG, "abortReliableWrite() - device: " + mDevice); 1847 if (mService == null || mClientIf == 0) return; 1848 1849 try { 1850 mService.endReliableWrite(mClientIf, mDevice.getAddress(), false, mAttributionSource); 1851 } catch (RemoteException e) { 1852 Log.e(TAG, "", e); 1853 } 1854 } 1855 1856 /** 1857 * @deprecated Use {@link #abortReliableWrite()} 1858 */ 1859 @Deprecated 1860 @RequiresBluetoothConnectPermission 1861 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) abortReliableWrite(BluetoothDevice mDevice)1862 public void abortReliableWrite(BluetoothDevice mDevice) { 1863 abortReliableWrite(); 1864 } 1865 1866 /** 1867 * Enable or disable notifications/indications for a given characteristic. 1868 * 1869 * <p>Once notifications are enabled for a characteristic, a {@link 1870 * BluetoothGattCallback#onCharacteristicChanged(BluetoothGatt, BluetoothGattCharacteristic, 1871 * byte[])} callback will be triggered if the remote device indicates that the given 1872 * characteristic has changed. 1873 * 1874 * @param characteristic The characteristic for which to enable notifications 1875 * @param enable Set to true to enable notifications/indications 1876 * @return true, if the requested notification status was set successfully 1877 */ 1878 @RequiresLegacyBluetoothPermission 1879 @RequiresBluetoothConnectPermission 1880 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) setCharacteristicNotification( BluetoothGattCharacteristic characteristic, boolean enable)1881 public boolean setCharacteristicNotification( 1882 BluetoothGattCharacteristic characteristic, boolean enable) { 1883 if (DBG) { 1884 Log.d( 1885 TAG, 1886 "setCharacteristicNotification() - uuid: " 1887 + characteristic.getUuid() 1888 + " enable: " 1889 + enable); 1890 } 1891 if (mService == null || mClientIf == 0) return false; 1892 1893 BluetoothGattService service = characteristic.getService(); 1894 if (service == null) return false; 1895 1896 BluetoothDevice device = service.getDevice(); 1897 if (device == null) return false; 1898 1899 try { 1900 mService.registerForNotification( 1901 mClientIf, 1902 device.getAddress(), 1903 characteristic.getInstanceId(), 1904 enable, 1905 mAttributionSource); 1906 } catch (RemoteException e) { 1907 Log.e(TAG, "", e); 1908 return false; 1909 } 1910 1911 return true; 1912 } 1913 1914 /** 1915 * Clears the internal cache and forces a refresh of the services from the remote device. 1916 * 1917 * @hide 1918 */ 1919 @UnsupportedAppUsage 1920 @RequiresBluetoothConnectPermission 1921 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) refresh()1922 public boolean refresh() { 1923 if (DBG) Log.d(TAG, "refresh() - device: " + mDevice); 1924 if (mService == null || mClientIf == 0) return false; 1925 1926 try { 1927 mService.refreshDevice(mClientIf, mDevice.getAddress(), mAttributionSource); 1928 } catch (RemoteException e) { 1929 Log.e(TAG, "", e); 1930 return false; 1931 } 1932 1933 return true; 1934 } 1935 1936 /** 1937 * Read the RSSI for a connected remote device. 1938 * 1939 * <p>The {@link BluetoothGattCallback#onReadRemoteRssi} callback will be invoked when the RSSI 1940 * value has been read. 1941 * 1942 * @return true, if the RSSI value has been requested successfully 1943 */ 1944 @RequiresLegacyBluetoothPermission 1945 @RequiresBluetoothConnectPermission 1946 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) readRemoteRssi()1947 public boolean readRemoteRssi() { 1948 if (DBG) Log.d(TAG, "readRssi() - device: " + mDevice); 1949 if (mService == null || mClientIf == 0) return false; 1950 1951 try { 1952 mService.readRemoteRssi(mClientIf, mDevice.getAddress(), mAttributionSource); 1953 } catch (RemoteException e) { 1954 Log.e(TAG, "", e); 1955 return false; 1956 } 1957 1958 return true; 1959 } 1960 1961 /** 1962 * Request an MTU size used for a given connection. Please note that starting from Android 14, 1963 * the Android Bluetooth stack requests the BLE ATT MTU to 517 bytes when the first GATT client 1964 * requests an MTU, and disregards all subsequent MTU requests. Check out <a 1965 * href="{@docRoot}about/versions/14/behavior-changes-all#mtu-set-to-517">MTU is set to 517 for 1966 * the first GATT client requesting an MTU</a> for more information. 1967 * 1968 * <p>When performing a write request operation (write without response), the data sent is 1969 * truncated to the MTU size. This function may be used to request a larger MTU size to be able 1970 * to send more data at once. 1971 * 1972 * <p>A {@link BluetoothGattCallback#onMtuChanged} callback will indicate whether this operation 1973 * was successful. 1974 * 1975 * @return true, if the new MTU value has been requested successfully 1976 */ 1977 @RequiresLegacyBluetoothPermission 1978 @RequiresBluetoothConnectPermission 1979 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) requestMtu(int mtu)1980 public boolean requestMtu(int mtu) { 1981 if (DBG) { 1982 Log.d(TAG, "configureMTU() - device: " + mDevice + " mtu: " + mtu); 1983 } 1984 if (mService == null || mClientIf == 0) return false; 1985 1986 try { 1987 mService.configureMTU(mClientIf, mDevice.getAddress(), mtu, mAttributionSource); 1988 } catch (RemoteException e) { 1989 Log.e(TAG, "", e); 1990 return false; 1991 } 1992 1993 return true; 1994 } 1995 1996 /** 1997 * Request a connection parameter update. 1998 * 1999 * <p>This function will send a connection parameter update request to the remote device. 2000 * 2001 * @param connectionPriority Request a specific connection priority. Must be one of {@link 2002 * BluetoothGatt#CONNECTION_PRIORITY_BALANCED}, {@link 2003 * BluetoothGatt#CONNECTION_PRIORITY_HIGH} {@link 2004 * BluetoothGatt#CONNECTION_PRIORITY_LOW_POWER}, or {@link 2005 * BluetoothGatt#CONNECTION_PRIORITY_DCK}. 2006 * @throws IllegalArgumentException If the parameters are outside of their specified range. 2007 */ 2008 @RequiresBluetoothConnectPermission 2009 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) requestConnectionPriority(int connectionPriority)2010 public boolean requestConnectionPriority(int connectionPriority) { 2011 if (connectionPriority < CONNECTION_PRIORITY_BALANCED 2012 || connectionPriority > CONNECTION_PRIORITY_DCK) { 2013 throw new IllegalArgumentException("connectionPriority not within valid range"); 2014 } 2015 2016 if (DBG) Log.d(TAG, "requestConnectionPriority() - params: " + connectionPriority); 2017 if (mService == null || mClientIf == 0) return false; 2018 2019 try { 2020 mService.connectionParameterUpdate( 2021 mClientIf, mDevice.getAddress(), connectionPriority, mAttributionSource); 2022 } catch (RemoteException e) { 2023 Log.e(TAG, "", e); 2024 return false; 2025 } 2026 2027 return true; 2028 } 2029 2030 /** 2031 * Request an LE connection parameter update. 2032 * 2033 * <p>This function will send an LE connection parameters update request to the remote device. 2034 * 2035 * @return true, if the request is send to the Bluetooth stack. 2036 * @hide 2037 */ 2038 @RequiresBluetoothConnectPermission 2039 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) requestLeConnectionUpdate( int minConnectionInterval, int maxConnectionInterval, int slaveLatency, int supervisionTimeout, int minConnectionEventLen, int maxConnectionEventLen)2040 public boolean requestLeConnectionUpdate( 2041 int minConnectionInterval, 2042 int maxConnectionInterval, 2043 int slaveLatency, 2044 int supervisionTimeout, 2045 int minConnectionEventLen, 2046 int maxConnectionEventLen) { 2047 if (DBG) { 2048 Log.d( 2049 TAG, 2050 "requestLeConnectionUpdate() - min=(" 2051 + minConnectionInterval 2052 + ")" 2053 + (1.25 * minConnectionInterval) 2054 + "msec, max=(" 2055 + maxConnectionInterval 2056 + ")" 2057 + (1.25 * maxConnectionInterval) 2058 + "msec, latency=" 2059 + slaveLatency 2060 + ", timeout=" 2061 + supervisionTimeout 2062 + "msec" 2063 + ", min_ce=" 2064 + minConnectionEventLen 2065 + ", max_ce=" 2066 + maxConnectionEventLen); 2067 } 2068 if (mService == null || mClientIf == 0) return false; 2069 2070 try { 2071 mService.leConnectionUpdate( 2072 mClientIf, 2073 mDevice.getAddress(), 2074 minConnectionInterval, 2075 maxConnectionInterval, 2076 slaveLatency, 2077 supervisionTimeout, 2078 minConnectionEventLen, 2079 maxConnectionEventLen, 2080 mAttributionSource); 2081 } catch (RemoteException e) { 2082 Log.e(TAG, "", e); 2083 return false; 2084 } 2085 2086 return true; 2087 } 2088 2089 /** 2090 * Request LE subrate mode. 2091 * 2092 * <p>This function will send a LE subrate request to the remote device. 2093 * 2094 * @param subrateMode Request a specific subrate mode. 2095 * @throws IllegalArgumentException If the parameters are outside of their specified range. 2096 * @return true, if the request is send to the Bluetooth stack. 2097 * @hide 2098 */ 2099 @RequiresBluetoothConnectPermission 2100 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) requestSubrateMode(@ubrateRequestMode int subrateMode)2101 public boolean requestSubrateMode(@SubrateRequestMode int subrateMode) { 2102 if (subrateMode < SUBRATE_REQUEST_MODE_BALANCED 2103 || subrateMode > SUBRATE_REQUEST_MODE_LOW_POWER) { 2104 throw new IllegalArgumentException("Subrate Mode not within valid range"); 2105 } 2106 2107 if (DBG) { 2108 Log.d(TAG, "requestsubrateMode() - subrateMode: " + subrateMode); 2109 } 2110 if (mService == null || mClientIf == 0) { 2111 return false; 2112 } 2113 2114 try { 2115 mService.subrateModeRequest( 2116 mClientIf, mDevice.getAddress(), subrateMode, mAttributionSource); 2117 } catch (RemoteException e) { 2118 Log.e(TAG, "", e); 2119 return false; 2120 } 2121 return true; 2122 } 2123 2124 /** 2125 * Request a LE subrate request. 2126 * 2127 * <p>This function will send a LE subrate request to the remote device. 2128 * 2129 * @return true, if the request is send to the Bluetooth stack. 2130 * @hide 2131 */ 2132 @RequiresBluetoothConnectPermission 2133 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) bleSubrateRequest( int subrateMin, int subrateMax, int maxLatency, int contNumber, int supervisionTimeout)2134 public boolean bleSubrateRequest( 2135 int subrateMin, 2136 int subrateMax, 2137 int maxLatency, 2138 int contNumber, 2139 int supervisionTimeout) { 2140 if (DBG) { 2141 Log.d( 2142 TAG, 2143 "bleSubrateRequest() - subrateMin=" 2144 + subrateMin 2145 + " subrateMax=" 2146 + (subrateMax) 2147 + " maxLatency= " 2148 + maxLatency 2149 + "contNumber=" 2150 + contNumber 2151 + " supervisionTimeout=" 2152 + supervisionTimeout); 2153 } 2154 if (mService == null || mClientIf == 0) { 2155 return false; 2156 } 2157 2158 try { 2159 mService.leSubrateRequest( 2160 mClientIf, 2161 mDevice.getAddress(), 2162 subrateMin, 2163 subrateMax, 2164 maxLatency, 2165 contNumber, 2166 supervisionTimeout, 2167 mAttributionSource); 2168 } catch (RemoteException e) { 2169 Log.e(TAG, "", e); 2170 return false; 2171 } 2172 return true; 2173 } 2174 2175 /** 2176 * @deprecated Not supported - please use {@link BluetoothManager#getConnectedDevices(int)} with 2177 * {@link BluetoothProfile#GATT} as argument 2178 * @throws UnsupportedOperationException on every call 2179 */ 2180 @Override 2181 @RequiresNoPermission 2182 @Deprecated getConnectionState(BluetoothDevice device)2183 public int getConnectionState(BluetoothDevice device) { 2184 throw new UnsupportedOperationException("Use BluetoothManager#getConnectionState instead."); 2185 } 2186 2187 /** 2188 * @deprecated Not supported - please use {@link BluetoothManager#getConnectedDevices(int)} with 2189 * {@link BluetoothProfile#GATT} as argument 2190 * @throws UnsupportedOperationException on every call 2191 */ 2192 @Override 2193 @RequiresNoPermission 2194 @Deprecated getConnectedDevices()2195 public List<BluetoothDevice> getConnectedDevices() { 2196 throw new UnsupportedOperationException( 2197 "Use BluetoothManager#getConnectedDevices instead."); 2198 } 2199 2200 /** 2201 * @deprecated Not supported - please use {@link 2202 * BluetoothManager#getDevicesMatchingConnectionStates(int, int[])} with {@link 2203 * BluetoothProfile#GATT} as first argument 2204 * @throws UnsupportedOperationException on every call 2205 */ 2206 @Override 2207 @RequiresNoPermission 2208 @Deprecated getDevicesMatchingConnectionStates(int[] states)2209 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 2210 throw new UnsupportedOperationException( 2211 "Use BluetoothManager#getDevicesMatchingConnectionStates instead."); 2212 } 2213 } 2214