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